Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re^4: [OT: JavaScript] JS remainder operation ('%')

by syphilis (Archbishop)
on Jan 18, 2024 at 12:59 UTC ( [id://11157076]=note: print w/replies, xml ) Need Help??


in reply to Re^3: [OT: JavaScript] JS remainder operation ('%')
in thread [OT: JavaScript] JS remainder operation ('%')

Man, this is weird! I have no idea what's going on

There's a lot to digest, but here's a few pointers that might make things a little easier.

Firstly, prior to perl-5.30.0, perl was prone to assign values to NVs incorrectly - so you can eliminate one source of frustration by not using a perl that's any older than 5.30.0.
(This problem did not afflict quadmath builds ($Config{nvtype} is '__float128').

Secondly, perl's print() function often lies about floating point values.
A good example is:
>perl -le "print 1.4/10;" 0.14

You might therefore deduce that 0.14 == 1.4/10, but you'd be wrong - and perl will happily inform you of that:
>perl -le "print 'wtf' if 0.14 != 1.4/10;" wtf

IOW, perl knowingly lies, whereas reputable languages like JavaScript, Raku, and Python (to name a few) will truthfully report that 1.4/10 is 0.13999999999999999, and perl will happily confirm the fact:
>perl -le "print 'ok' if 0.13999999999999999 == 1.4/10;" ok

The problem is that perl takes the correct 17-significant-digit representation and rounds it to a 15-bit-significant-digit number. Therefore, whenever 16 or 17 significant digits are required for correctness, perl fails to deliver.
I can't describe how stupid I think this choice was without resorting to expletives ...

The best you can do with perl is to use printf() to output 17 significant digits.
At least then you'll have a value that will survive the round trip. A perl floating point scalar $nv, survives the round trip if and only if the condition ("$nv" == $nv) is true. (Apart from NaNs of course.)

The other sane thing that JavaScript, Raku and Python (to name a few) do is to display the fewest possible digits needed for the round trip to succeed. (For this they utilize the ryu algorithm.
OTOH, if you get perl to printf() 17 significant digits (for correctness), you are often displaying more digits than are necessary.
For example:
>perl -le "printf '%.17g', 1e+23" 9.9999999999999992e+22

Sure - that survives the round trip, but so does "1e+23" - and it's "1e+23" that a smart print() implementation provides.
Perl will tell you that the condition (9.9999999999999992e+22 == 1e+23) is true, so it makes good sense to output the latter (as do JavaScript, Python and Raku).

If you're interested, you can use Math::Ryu's d2s() function to display the value of your perl NVs (doubles) using the minimum number of significant digits that are required for the round trip to succeed. (Just like JavaScript, Raku and Python !!)

HTH.

Cheers,
Rob

Replies are listed 'Best First'.
Re^5: [OT: JavaScript] JS remainder operation ('%')
by choroba (Cardinal) on Jan 18, 2024 at 13:07 UTC
    > whereas reputable languages like JavaScript, Raku, and Python

    Perl was born before these reputable languages existed. C++ outputs the same number as Perl:

    #include<iostream> int main(int argc, char** argv) { std::cout << 1.4/10 << std::endl; }
    Output:
    0.14

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      C++ outputs the same number as Perl

      I think there was, and probably still is to some extent, the view that if a 15-significant-digit representation isn't a good enough approximation, then it's up to the user to provide the code that does what's wanted.
      That might be just using printf "%.17g", ..., or using a certain library of your choice.

      Interestingly, every finite double can be expressed exactly in base 10 in no more than 767 mantissa digits and, if one wants to get anal about it, one can even get to see that exact representation by doing printf("%.767g", $val).
      And that will probably work on most perls (and also C, C++), unless you've got an ancient system:
      D:\>perl -le "printf '%.767g', 0.1;" 0.1000000000000000055511151231257827021181583404541015625 and D:\>perl -le "printf '%.767g', 2 ** -1074;" 4.94065645841246544176568792868221372365059802614324764425585682500675 +50727020875186529 9836361635992379796564695445717730926656710355939796398774796010781878 +12630071319031140 4527845817167848982103688718636056998730723050006387409153564984387312 +47339727316961514 0031715385398074126238565591171026658556686768187039560310624931945271 +59149245532930545 6544401127480129709999541931989409080416563324524757147869014726780159 +35523861155013480 3526493472019379026810710749170333222684475333572083243193609238289345 +83680601060115061 6980975307834227731832924790498252473077637592724787465608477820373446 +96995336470179726 7771758512566055119913150489110145103786273816725095583738973359899366 +48099411642057026 37090279242767544565229087538682506419718265533447265625e-324
      Mind you, I don't think I've ever found a practical use for this capability.

      Cheers,
      Rob
        Perl's weirdnesses are sometimes just inherited from it's host language C.

        Choroba didn't tell us if C has the same output too, but I remember the strange rules to numify ASCII strings to integers stemming from an atoi routine in C.

        use warnings; say "12x45"+1; __END__ Argument "12x45" isn't numeric in addition 13

        It's not that Larry came up with all of this on his own.

        FWIW I don't think this warning was always there...

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re^5: [OT: JavaScript] JS remainder operation ('%')
by LanX (Saint) on Jan 18, 2024 at 13:46 UTC
    > The problem is that perl takes the correct 17-significant-digit representation and rounds it to a 15-bit-significant-digit number. Therefore, whenever 16 or 17 significant digits are required for correctness, perl fails to deliver.

    Wouldn't it be better to s/perl/print/g here?

    I agree that print is kind of "lying", and I would love to see this unexpected behavior at least clearly documented in the perldocs.

    IMHO your statement is misleading as it is, because it's limited to output by print and doesn't effect internal representation.

    Edit

    Since print only affects the output one might consider adding something like print_truely feature, for future versions.

    Best with accompanying flags in printf for compatibility.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

      Wouldn't it be better to s/perl/print/g here?

      That would be better. (Some might even argue that "perl's interpolation" would be the more strictly accurate replacement - since all print() does is to output what perl has interpolated ... but that sort of thinking is way too moot for me to understand ;-)

      IMHO your statement is misleading as it is, because it's limited to output by print and doesn't effect internal representation.

      I don't quite follow that. The only problem is the print function (or perl's interpolation of NV's or whatever we should call it).
      There's no issue with perl's internal representation of doubles, since 5.30.0.

      Cheers,
      Rob
        > Some might even argue that "perl's interpolation

        That's a very good point!

        But it's not the interpolation, but more generally the stringification .

        use v5.12; use warnings; $a= 1-1e-16; printf "%.16f\n",$a; printf "%.16f\n",("".$a);

        0.9999999999999999 1.0000000000000000

        Consequently this should be documented for stringification, and print should reference it.

        Update

        Apart from mentions in perlglossary and overload there is not much on stringification to be found in perldocs :/

        https://perldoc.perl.org/perlglossary#stringification

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11157076]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (3)
As of 2024-05-26 05:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found