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

Though I suppose another view might be: can you judge the assignment to be successful, if you can never get the value you assigned, back, without it having been corrupted.

Yes - how do we conclusively determine whether the bug is in the assigning, or in the retrieving ?
I've been approaching this in a number of different ways, using POSIX, Inline::C and pack/unpack and it's always the same result.
On the x86 perl, whenever I check the value of the (supposed) SNAN, the output tells me that it's a QNAN. Yet the x64 perl always produces expected results.
It's very strange that identical code that behaves fine on x64 perl breaks on x86 perl - given that both of those perls have an ivtype of "long long" and an nvtype of "double".

Just now I've built x86 latest blead (5.25.2) with SvNVX() modified such that the addition of -0.0 is removed.
But it has made no difference.

However, I notice that the SvNVX macro that adds -0.0 is in an #ifdef PERL_DEBUG_COW block and I don't know if PERL_DEBUG_COW is defined.
I suspect that it's not defined because the test suite still produces the same results.

I should just file the bug report and let 'em work it out.

Cheers,
Rob

Replies are listed 'Best First'.
Re^10: unintentional conversion of signaling NaN to quiet NaN
by BrowserUk (Patriarch) on Jun 26, 2016 at 14:00 UTC
    Yes - how do we conclusively determine whether the bug is in the assigning, or in the retrieving ?

    The best I can think of is the IC code below. It returns an SV that has SvNOK set and the NV contains an SNaN bit pattern.

    It does two things that are a bit trick (and non-portable to earlier (<5.10) builds because of changes to perl's internal structures over the last several builds.)

    1. It assigns the bit pattern to the NV field of the SV directly, bypassing any/all internal macros that might mess with it.
    2. It points the PV pointer at the memory containing the NV allowing direct access from perl without unpack 'd'/'F'

    It *may* be possible to port it to gcc/32-bit?

    #! 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*snan2() { // Allocate a new SvPV with some junk pv data SV *ret = newSVpvn( "JustJunk", 8 ); // Assign the SNaN bit pattern directly to the NV field bypassing +any dodgy macros *(unsigned __int64*)&( ( (XPVNV*)ret->sv_any )->xnv_u.xnv_nv ) = i +; // Set the NOK flag SvNOK_on( ret ); // Now point the PV pointer directly at the NV memory so we can ac +cess it // (the NV) from perl without going through pack 'd'/'F' SvPV_set( ret, (char*)&( ( (XPVNV*)ret->sv_any )->xnv_u.xnv_nv ) ) +; // make it persist SvREFCNT_inc_void_NN( ret ); // Make sure noone can mess with it. SvREADONLY( ret ); return ret; } END_C ## Get an SNAN. my $snan2 = snan2(); ##Print its numeric value AND its bit pattern bypassing SVNV?() printf "%f\n%x\n", $snan2, unpack 'Q', $snan2; ## Same thing as the printf '%x', unpack 'Q' above ## that *might* work on 32-bit that doesn't have Q print scalar reverse unpack 'h*', $snan2; __END__ C:\test>snan-ic.pl 1.#SNAN0 7ff0000000000001 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.
      The best I can think of is the IC code below

      It allows me to get the same output as you, but I don't know that it proves the problem is in SvNV() because it also avoids sv_setnv().
      And I've been assuming that the problem is inside sv_setnv().

      I'll post a bug report (referencing this thread) later today and report the developments from that report back to this thread.

      In the meantime, I'm happy to continue this discussion - though my thick skull may continue to defy the penetration of the points you make ;-)

      With the code you just provided, running Devel::Peek::Dump($snan); crashes - which is a pity, because I would have liked to see more of what is in that variable.
      And print $snan; outputs the same as pack 'd' done on a signalling NaN (that has the 7ff0000000000001 format).
      The last line of code crashes for me (on a 32-bit perl that has quad formatting) - but that's no issue as the previous code had displayed:
      NaN 7ff0000000000001
      and it was nice to see 7ff0000000000001 for a change.

      The CCFLAGS setting in the I::C config has no impact afaict. (I assume it's a leftover from a previous exercise - and mention it only in case it should have provided something significant.)

      Cheers,
      Rob
        It allows me to get the same output as you, but I don't know that it proves the problem is in SvNV() because it also avoids sv_setnv().

        The problem is that none of the macros&functions just do what they are named to do; and it is impossible to work out exactly what they really do if you are not a compiler!

        For example. In the following version I replaced the direct assignment of the bit pattern to the NV field with sv_setnv(). It works(???), in as much as printing the value of the scalar as a float produces 1.#SNAN (for me); but the purpose of the exercise -- using the PV pointer to get at the NV memory direct -- fails, because as well as performing the assignment of the NV, the function also changes the type of the SV that I carefully construct as a SvPVNV, to ... something else, so all my shenanigans with the PV pointer are wasted.

        However, the fact that setting the bit pattern with sv_setnv() results in 1.#SNAN output from printf proves that it (on my system. ie. compiled with MSVCv???) isn't messing with the value on the way in; which puts the problems of viewing the bit pattern firmly in the pack'd'/'F' and thus SvNV????().

        With the code you just provided, running Devel::Peek::Dump($snan); crashes

        That unsurprising given what I'm doing with the SV.

        I was reluctant to draw this diagram as there is always gonna be some pedant that gonna come back with: "That's a lie! SV's don't look like that on version 5.2017 on a Raspberry Pie with a 65-bit LEG processor manufactured by the Cray Twins."

        Suffice to say, the following diagram is representational, and doesn't necessarily reflect any version of reality:

        Notionally, a fully loaded SV looks vaguely like this: SV*->[ SVANY ]->[ IV ] [ FLAGS ] [ NV ] [ PV ]->"some text" What my code above does is this: SV*->[ SVANY ]->[ IV ] [ FLAGS ] [ NV ]<-+ [ PV ]--+ Point the PV at the memory holding th +e NV.

        The problem is that, having messed with the pointer, I haven't fixed up SV_CUR and SV_LEN, so it is unsurprising that Devel::Peek gets confused. (I did warn it was trick! :)

        The CCFLAGS setting in the I::C config has no impact afaict.

        I set that in an attempt to get debug symbols that would to allow me to trace into the ICexample.DLL with the debugger. It was not successful.

        The overall lesson is that when you try to inspect the content of an NV via unpack 'd'/'F', something is causing its value to be loaded into a floating point register; and as soon as you do that, regardless of whether you perform any actual mathematical operation on it, the FPU will silent convert SNANs to QNaNs.

        The purpose of pointing the PV at the NV is to allow you to inspect the bit patterns without using unpack 'd'/'F'; and that seems to work for you.

        I'm surprised by the failure of the unpack 'h*' on a 32-bit perl; but if you're only concerned with 32-bit perls that have 'Q', then you don't need that anyway, so no matter.

        As for exactly what is causing unpack 'd'/'F' to load the value into an FP register -- there is certainly no reason for it to do so -- I don't have a clue, if it isn't that addition of -0.0.

        However, I will say that the addition of -0.0 makes no sense. The documented reason -- the "preservation of ieee negative zero" -- just doesn't stand up to scrutiny. The comment say:

        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 */

        Which means: if the value is positive zero, adding negative zero will leave it positive; but if it is already negative zero, it will remain negative.

        In other words; its a bloody noop! At least in terms of the signedness of zeros. But, the very action of loading the value into FPU register to perform that pointless noop, means that SNaNs will be converted to QNaNs!

        Ie. The very problem you are seeing and why it is such a tempting target for my accusation; even if ultimately that particular piece of code isn't being conditionally compiled.

        If it isn't the ultimate cause, it should still be removed; and you (they) need to look for something similar elsewhere.


        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.

        Having drawn my silly picture of the SV, it dawned on me that Dump() ought to work properly; because the high byte of the PV address should act as the required null byte terminator for the 'string' I'm pointing it at.

        So then it pointed the finger at SvPV_set() as messing with stuff other than just the pointer it purports to set; so what happens if I bypass that macro/function? The result is the code below which successfully allows you (me, on 64-bit MSVC built perl) to dump the SV with devel peek:

        #! 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*snan3() { // Allocate a new SvPV with some junk pv data SV *ret = newSVpvn( "JustJunk", 8 ); // Assign the SNaN bit pattern directly to the NV field bypassing +any dodgy macros *(unsigned __int64*)&( ( (XPVNV*)ret->sv_any )->xnv_u.xnv_nv ) = i +; // Set the NOK flag SvNOK_on( ret ); // Now point the PV pointer directly at the NV memory so we can ac +cess it // (the NV) from perl without going through pack 'd'/'F' // once again bypassing the API. (ret)->sv_u.svu_pv = (char*)&( ( (XPVNV*)ret->sv_any )->xnv_u.xnv_ +nv ); // make it persist SvREFCNT_inc_void_NN( ret ); // Make sure noone can mess with it. SvREADONLY( ret ); return ret; } END_C use Devel::Peek; ## Get an SNAN. my $snan = snan3(); ##Print its numeric value AND its bit pattern bypassing SVNV?() printf "%f\n%x\n", $snan, unpack 'Q', $snan; ## Same thing as the printf '%x', unpack 'Q' above ## that *might* work on 32-bit that doesn't have Q print scalar reverse unpack 'h*', $snan; Dump( $snan ); __END__ C:\test>snan-ic.pl ... Finished Build Compile Stage 1.#SNAN0 7ff0000000000001 7ff0000000000001 SV = PV(0xfbf60) at 0x157bc8 REFCNT = 1 FLAGS = (PADMY,NOK,POK,pNOK,pPOK) PV = 0x3db5d68 "\1\0\0\0\0\0\360\177"\0 CUR = 8 LEN = 16

        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.