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 …
January 3rd, 2007 on 9:14 am
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.
January 3rd, 2007 on 1:34 pm
Good examples, Steven. I figured it wouldn’t work like that in C.
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.
January 4th, 2007 on 7:08 am
Reminds me of the old:
var x = 0.1
var y = 0.2
alert(x+y);
Heh. =)