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