Hi all,

After some off-list discussion with Chris I have fixed his fix in longlong-double-fix branch. It seems to work, passes all tests and is more or less ready for merging to master.

Travis-CI is all green see https://travis-ci.org/PDLPorters/pdl/builds/77348194

All changes: https://github.com/PDLPorters/pdl/compare/master...longlong-double-fix#files_bucket

The main idea is a new type PDL_Anyval capable of keeping values of all types without loosing any precision. Like this:

typedef enum { PDL_B, PDL_S, PDL_US, PDL_L, PDL_IND, PDL_LL, PDL_F, PDL_D } pdl_datatypes;

typedef struct {
    pdl_datatypes type;
    union {
        PDL_Byte     B;
        PDL_Short    S;
        PDL_Ushort   U;
        PDL_Long     L;
        PDL_Indx     N;
        PDL_LongLong Q;
        PDL_Float    F;
        PDL_Double   D;
    } value;
} PDL_Anyval;

There are still some questions that I want to discuss here:

_*1/ "broken" C API (not that much, but still)*_

Using PDL_Anyval struct (instead of plain double) has some side effects concerning C API, namely:
PDL->put_offs(..)
PDL->get_offs(..)
PDL->get(..)
PDL->get_pdl_badvalue(..)

The relevant declarations in Core structure were not changed:
void (*put_offs)(pdl *pdl,PDL_Indx offs, PDL_Anyval val);
PDL_Anyval (*get_offs)(pdl *pdl,PDL_Indx offs);
PDL_Anyval (*get)(pdl *pdl,PDL_Indx *inds);
PDL_Anyval (*get_pdl_badvalue)(pdl *it);

however the old code like:
if( PDL->get_pdl_badvalue(a) == 0 )  { ... }
does not work anymore (it does not even compile) from obvious reason.

The new style should be
double bad_a;
ANYVAL_TO_CTYPE(bad_a, double, PDL->get_pdl_badvalue(a));
if( bad_a == 0 ) { ... }

BTW: via http://grep.cpan.me/ I have found only one Zaki's module that is affected (+of course possibly a lot of code not on CPAN)

There are also four newly introduced ANYVAL_* macros in pdl.h for handling PDL_Anyval from C code.
#define ANYVAL_TO_SV(outsv,inany) ...
#define ANYVAL_FROM_CTYPE(outany,avtype,inval) ...
#define ANYVAL_TO_CTYPE(outval,ctype,inany) ...
#define ANYVAL_EQ_ANYVAL(x,y) ...

Which will be a pain for those who want to write their modules/program so that it works with old PDL versions (without these macros) as well as with the new version. The only recommendation I have for this scenario is the following set of defines that has to be added to the C code for compatibility with older PDL.

#if PDL_CORE_VERSION < 12
#define ANYVAL_TO_SV(outsv,inany)    outsv = newSVnv((NV)(inany)
#define ANYVAL_FROM_CTYPE(outany,avtype,inval)    outany = (PDL_Double)(inval)
#define ANYVAL_TO_CTYPE(outval,ctype,inany)    outval = (ctype)(inany)
#define ANYVAL_EQ_ANYVAL(x,y)    (x == y)
#endif

_*2/ typemap & undef handling*_

I am not quite sure what is the expected behaviour when undef SV argument has to be converted to PDL_Anyval. Now I convert it into a invalid PDL_Anyval { type=-1, value.B=0 } which might not be the best idea.

The old behaviour was always (even for undef) use:
$var = (double)SvNV($arg)

The difference can be seen for example here:
  my $p = sequence(4);
  PDL::Core::set_c($p, [1], undef);
  print $p
The old behaviour is "undefined ... warning" + setting the value to 0 (zero). The new behaviour is to croak.

Of course it would be easy to change croak() to silently setting 0 value but I want to ask: what is correct/expected/best behaviour in this case?

We are talking here about a typemap so we do not have any context (e.g. type of a piddle where PDL_Anyval value will be later assigned) therefore the obvious suggestion for using badval for undef is not possible as it is not clear which badval.

_*3/ PDL data type auto-detection*_

Auto-detection (we have a SV and want to decide for the best PDL datatype) is implemented at couple of places. Based on perl's SV type (int/IV or double/NV) we decide for PDL_Long (on perl with 64bit int for PDL_LongLong) or PDL_Double.

Simplified code works like this:
  if (SvIOK(sv)) {
  #if IVSIZE == 8
      val = (PDL_LongLong)SvIV(sv)
  #else
      val = (PDL_Long)SvIV(sv)
  #endif
  } else if (SvOK(sv)) {
      val = (PDL_Double)SvNV(sv) /* tries also to convert strings */
  } else {
      // do some magick in undef case
  }

or even simpler:
  if (SvIOK(sv)) {
  #if IVSIZE == 8
      val = (PDL_LongLong)SvIV(sv)
  #else
      val = (PDL_Long)SvIV(sv)
  #endif
  } else {
      val = (PDL_Double)SvNV(sv) /* tries also to convert strings + undef */
  }

This kind of works but I am open to any comments/improvements.

BTW: let's have:
  $p = pdl( [ 1, 2, 3] ); # perl integers
  print $p;
Is it correct that type of $p is always Double? although LongLong would be more than enough?


Thanks in advance for any feedback.

--
kmx

------------------------------------------------------------------------------
_______________________________________________
pdl-devel mailing list
pdl-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/pdl-devel

Reply via email to