in reply to [Raku] Arcane treatment of doubles and rationals

I had thought that when raku compared a double with a rational, it would convert the double to its exact rational value, and then compare the 2 rationals.
Instead it apparently converts the rational to a double (rounding if necessary), and then compares the 2 doubles.


I've managed to satisfy myself that this is, in fact, what is going on. And based on this newfound wisdom, I've modified (and expanded) the original raku script that I provided so that it outputs no "not ok..." messages at all:
my $d_1 = 0.1e0; # double 0.1 ( == 3602879701896397/3602879701896396 +8 ) my $r_1 = 0.1; # rational 1/10 my $d_2 = 0.10000000000000001e0;# double, same value as $d_1 my $r_2 = 0.10000000000000001; # rational 10000000000000001/100000000 +000000000 # check that $d_1 == 3602879701896397/36028797018963968 say "not ok 0" if $d_1 != 3602879701896397/36028797018963968; # Check that $d_1 == 1 / 10 # This should be true because the RHS will be rounded to 0.1e0 say "not ok 1" if $d_1 != 1 / 10; # Check that $d_1 and $d_2 are assigned to exactly the same value: say "not ok 2" if $d_1 != $d_2; # Check that $r_ and $r_2 are assigned different values: say "not ok 3" if $r_1 == $r_2; # Although $r_1 and $r_2 are unequal, $d_1 should be equal to both $r_ +1 && $r_2. # This is because both $r_1 and $r_2 round to 0.1e0 # We check this, interchanging LHS and RHS operands in case that makes + a # difference: say "not ok 4" if ($d_1 != $r_1 && $d_1 != $r_2); say "not ok 5" if ($r_1 != $d_1 && $r_2 != $d_1); # Similarly $d_2 should be equal to both $r_1 and $r_2: say "not ok 6" if ($d_2 != $r_1 && $d_2 != $r_2); say "not ok 7" if ($r_1 != $d_2 && $r_2 != $d_2); # The following 9 rationals should all round to 0.1e0. # They equate to the 56-bit values: # 0.11001100110011001100110011001100110011001100110011001100E-3 # 0.11001100110011001100110011001100110011001100110011001101E-3 # 0.11001100110011001100110011001100110011001100110011001110E-3 # 0.11001100110011001100110011001100110011001100110011001111E-3 # 0.11001100110011001100110011001100110011001100110011010000E-3 # 0.11001100110011001100110011001100110011001100110011010001E-3 # 0.11001100110011001100110011001100110011001100110011010010E-3 # 0.11001100110011001100110011001100110011001100110011010011E-3 # 0.11001100110011001100110011001100110011001100110011010100E-3 say "not ok 8" if 14411518807585587/144115188075855872 != 0.1e0; say "not ok 9" if 57646075230342349/576460752303423488 != 0.1e0; say "not ok 10" if 28823037615171175/288230376151711744 != 0.1e0; say "not ok 11" if 57646075230342351/576460752303423488 != 0.1e0; say "not ok 12" if 3602879701896397/36028797018963968 != 0.1e0; say "not ok 13" if 57646075230342353/576460752303423488 != 0.1e0; say "not ok 14" if 28823037615171177/288230376151711744 != 0.1e0; say "not ok 15" if 57646075230342355/576460752303423488 != 0.1e0; say "not ok 16" if 14411518807585589/144115188075855872 != 0.1e0; # The following 2 rationals should not round to 0.1e0. # They equate to the 56-bit values: # 0.11001100110011001100110011001100110011001100110011001011E-3 (less +than 0.1 by 1ULP) # 0.11001100110011001100110011001100110011001100110011010101E-3 (great +er than 0.1 by 1ULP) say "not ok 17" if 57646075230342347/576460752303423488 == 0.1e0; say "not ok 18" if 57646075230342357/576460752303423488 == 0.1e0; # Each of the 11 rationals just provided should be unequal to each oth +er. # We check this for a few (selected) values of these rationals. say "not ok 19" if 14411518807585587/144115188075855872 == 57646075230 +342349/576460752303423488; say "not ok 20" if 14411518807585587/144115188075855872 == 14411518807 +585589/144115188075855872; say "not ok 21" if 14411518807585587/144115188075855872 == 57646075230 +342347/576460752303423488; say "not ok 22" if 14411518807585589/144115188075855872 == 57646075230 +342357/576460752303423488;
And I think I understand the reasons (which are specified in the comments) that this particular script produces no output at all.
I don't agree with some of those reasons, though I don't rule out the possibility that the case is, at least, arguable.
And the documentation I found was full of all sorts of nomenclature/notation that I didn't understand.

There are, however, some additional aspects that I believe are nothing other than "weird buggery" - and I'll eventually report these "additional aspects" to the appropriate forum unless someone here likes to show me the errors in my thinking.
For example:
> say 838861/8388608 0.10000002
How is that in any way accurate, helpful or useful ?
Neither as a rational nor a double does 838861/8388608 == 0.10000002
> say 838861/8388608 == 0.10000002 False > say 838861/8388608 == 0.10000002e0 False
The first interesting thing about the rational 838861/8388608 is that if you assign the value 0.1 to a 20-bit precision floating point data type, then the value assigned is exactly equivalent to 838861/8388608.
The second interesting thing is that, if you assign the value 0.10000002e0 to a 20-bit precision floating point data type, then you assign exactly the same value (ie 838861/8388608).
But, AFAIK, raku does not provide us with a 20-bit precision floating point type ... so why does it present us with a 20-bit precision floating point value ?
(I don't believe it would intentionally do that ... which is the reason that I'm thinking "bug".)
For mine, it would make far better sense to provide us with a value that can actually be verified:
> say 838861/8388608 == 0.10000002384185791e0 True
Even more tantalizing:
> say 56294995342131/562949953421312 0.0999999999999996 > say 56294995342131/562949953421312 == 0.0999999999999996 False > say 56294995342131/562949953421312 == 0.0999999999999996e0 False
All it had to do was provide one additional decimal place:
> say 56294995342131/562949953421312 == 0.09999999999999964e0 True
I have other similar examples.

Sorry - I probably should have placed my original post on this in the rants section.
I have no objections if someone wants to move this thread there.

Cheers,
Rob

Replies are listed 'Best First'.
Re^2: [Raku] Arcane treatment of doubles and rationals
by holli (Abbot) on Apr 04, 2021 at 13:16 UTC
    $ raku -e "say (838861/8388608).raku" 0.10000002384185791015625 $ raku -e "dd 838861/8388608" 0.10000002384185791015625 $ raku -e "say (838861/8388608).gist" 0.10000002 $ raku -e "say 838861/8388608" 0.10000002

    If you use say It will call gist on the object to say, trying to provide something human (easily) readable. Use dd instead or call the raku method.


    holli

    You can lead your users to water, but alas, you cannot drown them.
      Thanks - the raku and gist methods were what I needed to locate and comprehend.
      $ raku -e 'my $r = 3602879701896397/36028797018963968; say $r.raku; sa +y $r.raku == $r;' 0.1000000000000000055511151231257827021181583404541015625 True

      This is much more like what I expected of the raku language.
      I'm surprised that the gist method sacrifices accuracy so readily. Even 1/128 does not render the exact value, providing 0.007813 instead of 0.0078125.
      But that's a matter of choice - I now see nothing buggy happening there.

      As regards converting a double to its exact rational representation, this is quite simple in perl:
      $ perl -le 'printf "%.800g\n", 0.1' 0.1000000000000000055511151231257827021181583404541015625 $
      Is the same thing also as straightforward in raku ?
      I tried:
      $ raku -e 'printf "%.800g\n", 0.1e0' NaN $
      I've read documentation stating that raku's printf() expects that the last argument must be a string.

      Cheers,
      Rob
        $ perl -le 'printf "%.800g\n", 0.1'
        0.1000000000000000055511151231257827021181583404541015625


        You can do essentially the same in python3:
        $ python3 -c 'print ("%.800g" % 0.1e0)' 0.1000000000000000055511151231257827021181583404541015625
        It just remains to be seen if and how the same can be achieved in raku.
        I've now posted about this issue to the perl6-users list ( https://www.nntp.perl.org/group/perl.perl6.users/2021/04/msg9860.html )

        Of course, we don't have to ask for 800 digits for the particular value of 0.1, as it only requires 55 significant digits. But there are some double precision values that do need more.
        For example 4.4501477170144023e-308 requires 767 significant digits. I don't know of any value that requires more than 767 digits ... I'm confident (for some dubious definition of "confident") there aren't any.
        The value of 4.4501477170144023e-308 can be assigned with:
        C:\> perl -e "for(1022 .. 1074) { $x+= 2 ** -$_ }; printf '%.16e', $x"
        I've also found that when python3 and the mpfr library compare rationals and doubles, they do so by converting the double to its exact rational value, and then compare the 2 rationals.
        I haven't found any implementation (apart from raku) that converts the rational to its nearest double value, and then compares the doubles.
        (I've also mentioned this in the same perl6-users thread.)

        Cheers,
        Rob