in reply to Determining the minimum representable increment/decrement possible?

$fp2 = -8.2727285363069939e-293;; printf "% 25.17g\n", $fp2;; -7.9999999999999948e-293 ### WTF? ###


Hadn't noticed this before - a perl that produces that result is simply brainfucked.
I can reproduce that garbage on a perl-5.18.0 that I built with my Platform_SDK compiler on Windows 7:
C:\>perl -le "printf '%.16e', -8.2727285363069939e-293;" -7.9999999999999948e-293
Yet, according to the very same perl, the 2 values are entirely different:
C:\>perl -le "print scalar reverse unpack 'h*', pack 'd<', -7.99999999 +99999948e-293;" 83465a792c83e3b6 C:\>perl -le "print scalar reverse unpack 'h*', pack 'd<', -8.27272853 +63069939e-293;" 83498bf832dfdfab
UIM, this is not perl's fault. (But I am mistaken - see UPDATE and UPDATE2.) Perls built (from the same source) with gcc on the same machine do not suffer this problem.
I would not rely on the accuracy of *any* floating point result that such a perl produced.

UPDATE: After some double-checking I have found that the mingw-w64 gcc-4.7.x x64 compilers built perl-5.18.x with exactly the same problem as the Platform_SDK compiler build.
The problem went away with perl-5.20.0 onwards, but I don't know if that was because of changes to the perl source. I used later versions of gcc to build 5.20.0 onwards - and maybe that's what fixed the issue.

UPDATE2: The problem with 5.18.0 is simply that perl assigns the wrong value to the NV - instead of assigning the hex format 834a6aec8f941351, it assigns 83498bf832dfdfab, which by my calculation is off by 245,141,108,700,070 ULPs. (Such inaccuracies are usually quite small, and I was thrown by the large size of this one.)

Cheers,
Rob

Replies are listed 'Best First'.
Re^2: Determining the minimum representable increment/decrement possible? (Perl's scanf broken?)
by BrowserUk (Patriarch) on Jun 17, 2016 at 13:41 UTC

    Indeed. The previous post was done using 5.10.1; this is 5.22:

    C:\Program Files>\Perl22\bin\perl.exe \perl22\bin\p1.pl printf "% 25.17g\n", -8.2727285363069939e-293;; -8.2727285363069883e-293 ## manually reali +gned to highlight the difference. [0]{} Perl>

    Of more importance is the source of the numbers that apparently cannot be represented by 64-bit floating point. At least in perl.

    They are produced & output by the C++ code I'm optimising. I originally thought that the C++ math was producing denormals and C++ was outputting them unnormalised, but that does not seem to be the case:

    print join ' ', unpack 'a1 a11 a52', scalar reverse unpack 'b64', pack + 'd', $_ for -8.2727285363069939e-293, -8.2727285363069883e-293;; 1 00000110100 1010011010101110110010001111100101000001001101001100 1 00000110100 1010011010101110110010001111100101000001001101000111

    Even the latest perl seems just to be getting the conversion wrong!? Both numbers are representable with 64-bit Ieee754 numbers, so why Perl should silently convert one to the other is something of a mystery. Basically, Perl's (or the underlying CRT) input routine just seems to be broken.

    Update: The 5.22 perl I'm using is built with the same compiler and libraries as the C++ code; so this seems to be a PerlIO issue rather than a CRT issue.


    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.
      printf "% 25.17g\n", -8.2727285363069939e-293;;
      -8.2727285363069883e-293


      Yeah, I get the same with mingw-built x64 5.22.0.
      This is the perl bug I alluded to in my first post that mis-assigns values by up to a few ULPs.
      If you check the hex format (assigned by perl) of -8.2727285363069939e-293 you'll find:
      C:\_32>perl -le "print scalar reverse unpack 'h*', pack 'd<', -8.27272 +85363069939e-293;" 834a6aec8f94134c
      However, the correct hex format of -8.2727285363069939e-293 is 834a6aec8f941351.
      834a6aec8f94134c does in fact correspond to -8.2727285363069883e-293 - so perl is doing the conversion from internal form to decimal string correctly. It's just the initial conversion from decimal string to internal form that was incorrect.

      Sadly, no-one seems interested in fixing this - though I think that's because of the degree of difficulty rather than actual "disinterest".
      If this level of inaccuracy bothers you (as it does me) then one solution is to assign using POSIX::strtod:
      C:\_32>perl -MPOSIX -le "print scalar reverse unpack 'h*', pack 'd<', +POSIX::strtod('-8.2727285363069939e-293');" 834a6aec8f941351 C:\_32>perl -MPOSIX -le "printf '% 25.17g', POSIX::strtod('-8.27272853 +63069939e-293');" -8.2727285363069939e-293
      This way you put your faith in the C compiler and that seems to be a safer bet.
      (That's probably good enough, though I'd rather put my faith in the mpfr library.)

      Cheers,
      Rob
        Sadly, no-one seems interested in fixing this - though I think that's because of the degree of difficulty rather than actual "disinterest".

        I'm somewhat confused by that. Why not use the underlying CRTs sscanf() or strtod()?

        one solution is to assign using POSIX::strtod

        That's very useful! Thankyou.

        I'd rather put my faith in the mpfr library.

        For the most part, I'm only really using Perl as an interactive calculator for exploring the issues; the actual code is C++; and I'm doing some optimisations in x64 assembler.


        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.