Jeremy's Blog

Floating Point Craziness

by Jeremy on Jan.03, 2007, under Uncategorized

I was just going through my RSS Feeds and I stumbled upon 81.4 is evil through Planet PHP. I invite all of you developers to try this for yourself.

The original code:

<?php
$total = 100 – 81.4;
//$total should be 18.6
echo “18.6 == {$total}? ” .
($total == 18.6 ? ‘yes’ : ‘no’) .
“\n”;
var_dump(18.6 – $total);
?>

You’ll get a “no” and var_dump will give you float(-7.1054273576E-015) … Talk about the precision being a little awkward.

Now if you thought that was weird, check this out.

Look at what happens when you type-cast $total to a string before doing the arithmetic:

<?php

header(”Content-Type: text/plain”);

$total = 100 – 81.4;
echo “18.6 == {$total} ? ” . ((string) $total == 18.6 ? ‘yes’ : ‘no’) . “\n”;
echo (string) $total – 18.6;

?>

For me:

18.6 == 18.6 ? yes
0

Does type-casting float variables to strings before performing the math actually increase floating point precision, in PHP? I found this kind of odd …

:

3 comments for this entry:

  • Steven

    First one in Python:

    >>> total = 100 - 81.4
    >>> 18.6 == total
    False
    >>> 18.6 - total
    7.1054273576010019e-15
    >>> _ + 18.6 + 81.4
    100.00000000000001

    And the second:

    >>> total = 100 - 81.4
    >>> print "18.6 == %f ? %s" % (total, "yes" if str(total) == 18.6 else "no")
    18.6 == 18.600000 ? no
    >>> print "18.6 == %f ? %s" % (total, "yes" if str(total) == str(18.6) else "no")
    18.6 == 18.600000 ? yes

    For the first one, it just looks like a common floating point error. As for the second, I doubt casting to a string is “increasing the precision.” I believe that casting to a string is actually decreasing it, which removes the excess “garbage” decimal places.

    Oh, and just for the hell of it (and for some C practice) here it is in C:

    #include
    #include
    #include

    int main(int argc, char **argv)
    {
    float total;
    double total_d;
    char total_s[16];
    char total_d_s[32];
    char string_18_6[16];
    char string_18_6_d[32];

    total = 100 - 81.4f;
    total_d = 100 - 81.4;

    printf("18.6f == total ? %s\n", (18.6f == total ? "yes" : "no"));
    printf("18.6 == total_d ? %s\n", (18.6 == total_d ? "yes" : "no"));

    /* make the strings */
    sprintf(total_s, "%15f", total);
    sprintf(total_d_s, "%35f", total_d);
    sprintf(string_18_6, "%15f", 18.6f);
    sprintf(string_18_6_d, "%35f", 18.6);

    printf("strcmp(\"18.6f\", \"total_s\") ? %s\n", (strcmp(string_18_6, total_s) ? "yes" : "no"));
    printf("strcmp(\"18.6\", \"total_d_s\") ? %s\n", (strcmp(string_18_6_d, total_d_s) ? "yes" : "no"));

    return 0;
    }

    Which prints:
    18.6f == total ? no
    18.6 == total_d ? no
    strcmp(”18.6f”, “total_s”) ? no
    strcmp(”18.6″, “total_d_s”) ? no

    This is unsurprising, as PHP and other dynamic languages have their own ways of comparison.

  • Jeremy

    Good examples, Steven. I figured it wouldn’t work like that in C.

    I believe that casting to a string is actually decreasing it, which removes the excess “garbage” decimal places.

    Yeah, technically this is true, but it gets the job done. Of course, if you really need the additional floating point precision, you can just use the BCMath extension, which is bundled with PHP, now.

    $total = bcsub(100, 81.4, 1);
    echo “18.6 == {$total} ? ” . ($total == 18.6 ? “yes” : “no”) . “\n”;
    echo $total – 18.6;

  • James

    Reminds me of the old:

    var x = 0.1
    var y = 0.2

    alert(x+y);

    Heh. =)

Trackbacks / Pingbacks

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!