in reply to Re^2: unintentional conversion of signaling NaN to quiet NaN
in thread unintentional conversion of signaling NaN to quiet NaN

As soon as the value is loaded into an FP register, it gets silently converted to a QNan

I was thinking that when we do:
$packed = pack 'd', '7ff0000000000001'; $d = unpack 'h16', $packed;
there would be no loading of the double into an FP register.
But I guess that thinking must be wrong as $d is definitely set to '7ff8000000000001', on my Windows box at least.

On my Linux box (also little-endian), however, there's no such problem and I can assign '7ff0000000000001' just fine.
Is this difference a Windows v Linux thing ? ... or is it to be explained in terms of different hardware ?

Do you know of any way that one can assign the value '7ff0000000000001' on Windows ? (I haven't found a way, yet.)

Here's a nice Perl demo (utilising the union you provided) of the Windows/Linux discrepancy:
use warnings; use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; #ifdef _WIN32 typedef __int64 BIGGUN; #else /* "long" is 64-bit on my linux box */ typedef long BIGGUN; #endif typedef union { double d; BIGGUN i; } BITS64; void bytes(double d) { int i; void * p = &d; for(i = 7; i >=0; i--) printf("%02x", ((unsigned char*)p)[i]); printf("\n"); } double get_snan () { BITS64 x; x.i = 0x7ff0000000000001; bytes(x.d); return x.d; } EOC print doubleToHex(get_snan()); sub doubleToHex { scalar reverse unpack 'h16', pack 'd', $_[0] }
On Windows it outputs:
7ff0000000000001
7ff8000000000001

On Ubuntu it outputs:
7ff0000000000001
7ff0000000000001

Cheers,
Rob

Replies are listed 'Best First'.
Re^4: unintentional conversion of signaling NaN to quiet NaN
by BrowserUk (Patriarch) on Jun 24, 2016 at 17:31 UTC

    Doesn't this work for you?:

    $d = unpack 'd', pack 'Q', 0x7ff0000000000001;; print $d;; 1.#SNAN printf "%f\n", $d;; 1.#SNAN0

    If inline C has to be involved, do it this way:

    #! perl -slw use strict; use Config; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => 'snanic', CLEAN_AFTER_BUILD =>0; #ifdef _WIN32 typedef __int64 BIGGUN; #else /* "long" is 64-bit on my linux box */ typedef long BIGGUN; #endif typedef union { double d; BIGGUN i; } BITS64; SV*get_snan() { BITS64 x; x.i = 0x7ff0000000000001; return newSVnv( x.d ); } END_C sub doubleToHex { scalar reverse unpack 'h16', pack 'd', $_[0] } my $snan = get_snan(); printf "%f\n", $snan; print doubleToHex( $snan );

    Prints:

    C:\test>snan-ic.pl 1.#SNAN0 7ff0000000000001

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice. Not understood.
      Doesn't this work for you?:

      Well - it doesn't work for me with 32-bit (gcc-5.3.0) builds of perl.
      I was looking at 32-bit perl-5.24.0.:
      C:\_32>perl -V:archname archname='MSWin32-x86-multi-thread-64int'; C:\_32>perl -le "print $]"; 5.024000 C:\_32>perl -le "$d = unpack 'd', pack 'Q', 0x7ff0000000000001;print $ +d; printf '%f', $d;" NaN NaN
      That output doesn't tell us anything about the type of NaN we have. However, POSIX::issignaling tells us that $d is not signalling, and printf '%x\n', unpack 'Q', pack 'd', $d; outputs 7ff8000000000001.

      Things look ok for my 64-bit (gcc-5.3.0) builds of perl.
      With 64-bit perl-5.24.0 the output is the same as just given.
      But this time POSIX::issignaling tells us that $d is signalling and printf '%x\n', unpack 'Q', pack 'd', $d; outputs 7ff0000000000001.

      A regards your Inline::C script, the output is as expected given the above results.
      32-bit perl-5.24.0 ouptuts:
      NaN 7ff8000000000001
      64-bit perl-5.24.0 outputs:
      NaN 7ff0000000000001
      When it comes to Strawberry Perls 5.24.0 (which were built using gcc-4.9.2), I find that both 32-bit and 64-bit are buggy. Your I::C script outputs:
      NaN 7ff8000000000001
      for both.

      I'll try to investigate the problem a little better before I submit any bug reports. It may be that I can tie the behaviour to bugs in the mingw-w64 ports of gcc.

      Cheers,
      Rob

        The problem is this definition of SvNVX from sv.h:

        /* Need -0.0 for SvNVX to preserve IEEE FP "negative zero" because +0.0 + -0.0 => +0.0 but -0.0 + -0.0 => -0.0 */ # define SvIVX(sv) (0 + ((XPVIV*) SvANY(sv))->xiv_iv) # define SvUVX(sv) (0 + ((XPVUV*) SvANY(sv))->xuv_uv) # define SvNVX(sv) (-0.0 + ((XPVNV*) SvANY(sv))->xnv_u.xnv_nv)

        Their attempts to "preserve IEEE FP "negative zero"", performs + -0.0, which will *always* force SNaN to QNaN.

        That said, there is conditional code for the 'F' and 'D' templates in pp_pack.c that bypasses SvNV (which uses SvNVX() if SvNOK is set) in favour of sv_2nv(), which ought to kick in on mingw builds:

        #ifdef __GNUC__ /* to work round a gcc/x86 bug; don't use SvNV */ anv.nv = sv_2nv(fromstr); #else anv.nv = SvNV(fromstr); #endif

        But that conditional is not present in 'd' template.

        And in any case, sv_2nv() goes right ahead and uses SvNVX() if SvNOKp is set anyway:

        Perl_sv_2nv_flags(pTHX_ SV *const sv, const I32 flags) { dVAR; if (!sv) return 0.0; if (SvGMAGICAL(sv) || SvVALID(sv) || isREGEXP(sv)) { /* FBMs use the space for SvIVX and SvNVX for other purposes, and +use the same flag bit as SVf_IVisUV, so must not let them cache NVs +. Regexps have no SvIVX and SvNVX fields. */ const char *ptr; if (flags & SV_GMAGIC) mg_get(sv); if (SvNOKp(sv)) return SvNVX(sv);

        so I don't quite see what purpose the conditional code above achieves ?

        Bottom line: There is a bug -- or a combination of bugs -- that is preventing you getting your hands on an SNAN; but I don't think that it will be a quick fix.

        Your best bet if you want to 'cure' the problem yourself is to bypass the Perl macros completely. The following won't work pre-5.10, and may not work on some later builds. (The latest version of 32-bit perl I have with inline installed is 5.8.9 and it doesn't work there.) And remember, if you touch the returned value with pack it will probably screw with it again. :

        #! perl -slw use strict; use Config; use Inline C => Config => BUILD_NOISY => 1, CCFLAGS => $Config{ccflags +}." -DDEBUG=1"; use Inline C => <<'END_C', NAME => 'ICexample', CLEAN_AFTER_BUILD =>0 +; static const unsigned __int64 i = 0x7ff0000000000001; SV*get_snan() { SV *nv = newSVnv(0.0); *(unsigned __int64*)&( ((XPVNV*) SvANY(nv))->xnv_u.xnv_nv ) = i; return nv; } END_C sub doubleToHex { scalar reverse unpack 'h16', pack 'd', $_[0] } my $snan = get_snan(); printf "%f\n", $snan; print doubleToHex( $snan );

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice. Not understood.