http://qs321.pair.com?node_id=1083247

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

So value 1 is 14.4 and value 2 is 14.4, why is this evaluating to false?? Maybe I'm crazy.
my $value1 = 14.4; print "value1 = $value1\n"; my $value2 = 10 + 14.4 - 10; print "value2 = $value2\n"; if ( $value1 == $value2 ) { print "value 1 equals value 2\n"; } else { print "value 1 does not equal value2\n"; }

Replies are listed 'Best First'.
Re: Okay! What!?!?!?
by davido (Cardinal) on Apr 23, 2014 at 06:26 UTC

    On my system (Perl5.18.2):

    #!/usr/bin/env perl printf "%.49f\n", 14.4; printf "%.49f\n", 10 + 14.4 - 10;

    ...outputs...

    14.4000000000000003552713678800500929355621337890625 14.3999999999999985789145284797996282577514648437500

    And (GNU C++).....

    #include <iostream> #include <iomanip> using namespace std; int main () { cout << setprecision(51) << endl; cout << 14.4 << endl; cout << 10 + 14.4 - 10 << endl; return 0; }

    ...outputs...

    14.4000000000000003552713678800500929355621337890625 14.39999999999999857891452847979962825775146484375

    And (GNU C)

    #include <stdio.h> int main (void) { printf("%.49f\n", 14.4); printf("%.49f\n", 10.0 + 14.4 - 10.0); return 0; }

    ...produces this output:

    14.4000000000000003552713678800500929355621337890625 14.3999999999999985789145284797996282577514648437500

    This is because the decimal value 14.4 cannot be perfectly represented as a fraction in the form of n/(2^m), and therefore has a non-terminating binary expansion.

    The code I've shown above is first Perl, and second C++, and the third, C, just to illustrate that this is not a problem specific to just Perl. Here's yet another link that attempts to explain it: Re: shocking imprecision

    Update: Here's a Lisp (Racket, a Scheme derivative) example as well:

    #!/usr/bin/env racket #lang racket (displayln (~a 14.4 #:width 15 #:pad-string "0")) (displayln (~a (- (+ 10 14.4) 10) #:width 15 #:pad-string "0")) (= 14.4 (- (+ 10 14.4) 10))

    ...outputs...

    14.400000000000 14.399999999999 #f

    (The "#f" means "false"; 14.40 doesn't equal 14.399...)

    JavaScript (minimal example):

    alert(10+14.4-10);

    The pop-up displays: "14.399999999999999"

    Python:

    >>> print "%.49f"%14.4 14.4000000000000003552713678800500929355621337890625 >>> print "%.49f"%(10+14.4-10) 14.3999999999999985789145284797996282577514648437500

    Ruby:

    $ ruby -e 'printf "%.49f\n%.49f\n",14.4, 10+14.4-10' 14.4000000000000003552713678800500929355621337890625 14.3999999999999985789145284797996282577514648437500

    SQL (sqlite):

    sqlite> select 14.4 - (10 + 14.4 - 10); 1.77635683940025e-15

    Couldn't remember enough Pascal. "Go" must optimize away the math, because while it does produce an imprecise representation of 14.4, it produces the same imprecise representation for both 14.4, and 10+14.4-10. (GO):

    package main import "fmt" func main() { fmt.Printf("%.49f\n",14.4) fmt.Printf("%.49f\n", 10+14.4-10) } --------- 14.4000000000000003552713678800500929355621337890625 14.4000000000000003552713678800500929355621337890625 Program exited.

    Update: I left out Java because node lengths at PerlMonks are restricted to 64k (j/k -- I just don't know any Java)


    Dave

      It's been about 15 years since I've written any Pascal, but here you go...

      program floating; var A, B :real; begin A := 14.4e0; B := 10.0e0 + 14.4e0 - 10.0e0; writeln( A ); writeln( B ); if A = B then writeln('they are equal') else writeln('they differ'); end.

      Compiled using Free Pascal Compiler 2.6.2-5 (i386), I get the following output:

      1.44000000000000E+001 1.44000000000000E+001 they are equal

      I don't know if FPC is especially smart at optimizing the mathematical expressions, or especially smart at DWIM output and comparisons on floating point numbers. But it's being smart somehow.

      use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

        This could be explained by the compiler knowing to perform the constant folding in an order that reduces the risk of loss of precision.

        So do the addition and subtraction as separate steps.

        - tye        

        There must be some behind the scenes DWIMery, because according to the documentation, FreePascal's "real" type maps directly to a standard IEEE 754 "double" on most architectures, and as such, should suffer from the same shortcomings of representing floating point numbers in base 2.


        Dave

      Go suffers from this problem too. It uses an arbitrary number representation for constants only, which are computed at compile-time.

      For example, the following code:

      package main import "fmt" func main() { x := 14.4; y := 10.0; fmt.Printf("%.49f\n", x) fmt.Printf("%.49f\n", x+y-y) }
      Outputs:
      14.4000000000000003552713678800500929355621337890625 14.3999999999999985789145284797996282577514648437500

      "I just don't know any Java"

      Me too (not really). So a i used a GUI:

      Update: fixed typo.

      package Perlmonks; public class Testomato { public static void main(String[] args) { System.out.printf("%.49f\n",14.4); System.out.printf("%.49f\n", 10+14.4-10); } }

      I get:

      14,4000000000000000000000000000000000000000000000000 14,3999999999999990000000000000000000000000000000000

      Regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

Re: Okay! What!?!?!?
by soonix (Canon) on Apr 23, 2014 at 07:09 UTC
Re: Okay! What!?!?!?
by Anonymous Monk on Apr 23, 2014 at 02:14 UTC
Re: Okay! What!?!?!?
by LanX (Saint) on Apr 23, 2014 at 00:47 UTC
    See eq and Equality Operators

    Update: Sorry, misread code!

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      I understand that 'eq' is better, but this question came from a horribly designed test and the '==' operator has to be used.

      I think they should be equal but the code isn't coming out as equal?

      So for this test ( actual job test question ), what's the proper answer?
        Sorry for misreading your question.

        It's (again) a floating point problem.

        DB<149> $a=14.4 => "14.4" DB<150> $b=10+$a-10 => "14.4" DB<151> $a-$b => "1.77635683940025e-15"

        The mantissa does loose some digits at the end after adding 10 and substracting doesn't bring them back.

        A proper test is either to stay integer from the beginning till the end or to calculate the difference and to compare against a threshold (trickier).

        Please note how 14.5 doesn't have this problem since 1/2 is a power of 2 (i.e. only 0s are lost when shifting the mantissa)

        update
        see Re: eq vs == and Humans have too many fingers for more details

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: Okay! What!?!?!?
by sundialsvc4 (Abbot) on Apr 23, 2014 at 02:09 UTC

    I don’t rightly know what the (stringwise ...) eq operator would do in this obviously-numeric case, and I would not want to find out, even if someone proclaimed that it “worked.”

    Clearly, this is just a prototypical example of why you can never (in any programming language other than COBOL) test a fractional number for “exact equality.”   Even if the binary value rounds to exactly the same sequence of printed digits, and thus “to our human eyes” is ‘the same number,’ it is the binary values that are being compared by the computer, and these are almost certain to be “unequal.”

      Clearly, this is just a prototypical example of why you can never (in any programming language other than COBOL) test a fractional number for “exact equality.”

      Only if by "fractional number" you actually mean floating-point number. It works fine with actual fractional numbers:

      $ perl6 -e 'say 10 + 14.4 - 10 == 14.4' True

      (Perl 6 stores those as actual fractions, and does exact math with them; you only get floating points if the fractions overflow, or if you request them with the scientific notation 14.4e0).

        He said "programming language," not multi-decade performance art. COBOL has users.