http://qs1969.pair.com?node_id=1168204

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

$hash->{TGT}=3335.9999999999995; print $hash->{TGT}; Actual Result:- 3336 Excepted Result:- 3335.9999999999995
  • Comment on Variables are automatically rounded off in perl

Replies are listed 'Best First'.
Re: Variables are automatically rounded off in perl
by AnomalousMonk (Archbishop) on Jul 21, 2016 at 07:03 UTC
Re: Variables are automatically rounded off in perl
by syphilis (Archbishop) on Jul 21, 2016 at 07:35 UTC
    Yes - I think this is generally considered to be a perl bug. (That's how I, at least, regard it.)
    If perl's nvtype is 'double', perl will print floating point values rounded to 15 decimal digits - whereas the correct thing would be to round them to 17 decimal digits.
    Hence the surprising result that you see.

    Perl will correctly tell you that 3336.0 != 3335.9999999999995, yet will print both of those values identically (as 3336) ... which is rather absurd.

    If you want to see the values rounded to 17 decimal digits then the best solution currently is to printf() 17 decimal digits - eg:
    printf "%.16e\n", 3335.9999999999995;
    Cheers,
    Rob

      It is not a bug that Perl rounds the display of numeric values to a number of digits that is fewer than the number of digits of precision that the internal floating point format achieves. It is an intentional feature due to the fact that you get even more complaints when a computation should yield 1/100 but the result gets displayed as "0.01000000000000002" (than the number of complaints for such not being "== 0.01").

      Over time there have been adjustments as to how much difference there should be between the precision of the local floating point format vs. precision of Perl's display of numbers. But the (non-zero) difference should remain.

      Wanting to see about 17 '9's or '0's near the end of a displayed number indicates a rather unrealistic expectation. 15 digits of accuracy is more than enough to notice that somebody misplaced a single grain of sand from your enormous beach. It is not important to inform a human that a single grain of sand was lost from the beach. The vast majority of situations are such that the loss of a single grain of sand from the beach is utterly insignificant. If you are in one of those rarefied situations where the exact number of grains of sand on your huge beach must not be off by even 1, then you shouldn't just be using mundane floating point.

      - tye        

        It is not a bug that Perl rounds the display of numeric values to a number of digits that is fewer than the number of digits of precision that the internal floating point format achieves

        It depends upon how you measure that "number of digits".
        Sure - if you allow more than 15 decimal digits then you lose the guarantee that different strings will assign different values. (It's possible that 2 different 16-digit strings will assign to the same value.)
        But there are a vast number of values that cannot be assigned if you restrict yourself to 15 decimal digits.
        To be able to assign each and every value you need 17 decimal digits, minimum.

        To me the sane thing is to have print() deliver a value that, when assigned back to a scalar, will result in the same value.

        I think the following is rather pathetic:
        C:\>perl -le "print sqrt(3);" 1.73205080756888 C:\>perl -le "print 'WTF' if 1.73205080756888 != sqrt(3);" WTF
        Why do we have to put up with that sort of anomaly when it could be eradicated by simply providing just *2* more digits of precision ?
        Is 17 decimal digits really that much more unreadable than 15 digits ?

        you get even more complaints when a computation should yield 1/100 but the result gets displayed as "0.01000000000000002"

        I don't think that's the right example.
        For me, I have to request 18 decimal digits of precision to get the trailing "2", and being surprised by that sort of result when more than 17 digits have been requested is a "user bug".
        The "correct" precision is 17 decimal digits - not 18.
        C:\>perl -le "printf '%.16e', 1/100" 1.0000000000000000e-002 C:\>perl -le "printf '%.17e', 1/100" 1.00000000000000002e-002
        A better example is:
        C:\>perl -le "printf '%.16e', 0.1" 1.0000000000000001e-001
        This happens because 0.10000000000000001 and 0.1 have the same internal floating point representation, and that representation is closer to 0.10000000000000001 than 0.1.
        I actually don't have any problem with that - but if we want to avoid such surprising results, there's a more refined way of dealing with this type of issue than simply getting the chainsaw out and hacking the tail off everything.

        I am told that python, for example, utilises this refinement.
        That is, python prints out 17 decimal digits but still prints 0.1 out as '0.1'.

        Update: Current bug report regarding this issue is here

        Cheers,
        Rob
Re: Variables are automatically rounded off in computers
by Anonymous Monk on Jul 21, 2016 at 06:51 UTC

    Yes, unless you take care to use something like bignum in perl, floating point numbers in computers are true to their nature, approximations

Re: Variables are automatically rounded off in perl
by perlfan (Vicar) on Jul 21, 2016 at 14:46 UTC
Re: Variables are automatically rounded off in perl
by bart (Canon) on Jul 24, 2016 at 10:48 UTC
      Somebody had to mention it. 0.30000000000000004.com

      But that's a different issue. That one's about getting a result of 0.30000000000000004 when a result of 0.3 is "expected".

      In the OP's case, a result of 3336 was obtained though the expected (and correct) result is 3335.9999999999995.

      Cheers,
      Rob

      TLDR: It's not just perl. It's (virtually) every computer language out there."

      Exactly. JavaScript works the same way. I usually avoid working with floats whenever possible. If I have to get a precise result, then I multiply my number by 1000 to push the decimal point to the right. Whatever comes after the decimal point gets rounded, and it's usually garbage.

        I've done some testing with large numbers and floats in Perl and JavaScript. Here are the results:

        use strict; use warnings; my $NUM_A = 3335.999999999995; my $NUM_B = 3335.99999999995; # Normally, a 64-bit float can have 15 significant digits, # which is the mantissa of a number. If you try to write # 16 significant digits, you will lose precision!! And # that's what happens with $NUM_A. It has 16 significant # digit, and that's too long! # In JavaScript, you can store 3335.9999999999995 which # has 17 significant digits, but the numbers turns into 3336 # once you squeeze in another '9' in that list! # # In JavaScript, the largest mantissa you can have is: # 9007199254740992. If you try to add 1 to this number, # then the addition won't register, because it gets rounded # back to the original number. lol # # In JavaScript, if you try to add # 3335.9999999999995 + 0.0000000000001, it is equal to: # 3335.9999999999995 # So the addition won't even register, because it considers # that addition insignificantly small. The result won't change. # # In perl, it turns into 3336. lol # print " ADDITION: ", 3335.9999999999995 + 0.0000000000001, "\n"; print " SUBTRACT: ", 3335.9999999999995 - 0.0000000000001, "\n"; # Let's see what perl will do with this large number: print " BIGGEST JAVASCRIPT NUMBER: ", 9007199254740992, "\n"; print " BIGGEST JAVASCRIPT NUMBER: ", 900719925474099, "\n"; print " BIGGEST JAVASCRIPT NUMBER: ", 90071992547409, "\n"; print " BIGGEST JAVASCRIPT NUMBER: ", 9007199254740, "\n"; print " BIGGEST JAVASCRIPT NUMBER: ", 900719925474, "\n"; # "print" won't display the last significan digit. # so let's try this instead: if (9007199254740988 > 9007199254740987) { print "1 BIGGER OK.\n" } # + prints fine if (9007199254740989 > 9007199254740988) { print "2 BIGGER OK.\n" } # + prints fine if (9007199254740990 > 9007199254740989) { print "3 BIGGER OK.\n" } # + prints fine if (9007199254740991 > 9007199254740990) { print "4 BIGGER OK.\n" } # + prints fine if (9007199254740992 > 9007199254740991) { print "5 BIGGER OK.\n" } # + prints fine if (9007199254740993 > 9007199254740992) { print "6 BIGGER OK.\n" } # + <<this won't print! if (9007199254740994 > 9007199254740993) { print "7 BIGGER OK.\n" } # + prints fine if (9007199254740995 > 9007199254740994) { print "8 BIGGER OK.\n" } # + prints fine if (9007199254740996 > 9007199254740995) { print "9 BIGGER OK.\n" } # + <<this won't print! # See, as we start heading above that number, # we have problems in perl as well. # You can't tell whether a number is bigger or smaller, # because the last digit get rounded when it is stored in memory. # Okay, let's talk about divisions and remainders for a sec... print " NUM_A = $NUM_A \n NUM_B = $NUM_B\n"; my $R = $NUM_B % 3330; print " NUM_B % 3330 = $R (This should be 5.99999999995, but due to t +he strange way 64-bit floats are stored, it gets \"corrupted.\" So, n +ormally you'd get 5.9999999999499778. But in perl you don't even get +that result. You just get 5.)\n\n"; # So, in order to get the same result you would get # in JavaScript, you would call FMOD() function (see below) # instead of using the % (mod) operator: print " FMOD(NUM_B, 3330) = ", FMOD($NUM_B, 3330), "\n\n"; # # This function produces the same result as the % operator # in JavaScript: C = A % B; # # Usage: C = FMOD(A, B) # sub FMOD { my ($A, $B) = @_; return $A - int($A / $B) * $B; }

        No, it doesn't. JavaScript does not truncate digits. Try it:

        javascript:alert(3335.9999999999995)