SitePoint Sponsor |
|
User Tag List
Results 1 to 13 of 13
Thread: Have I uncovered a PHP bug?
-
Jun 25, 2007, 11:20 #1
- Join Date
- May 2007
- Posts
- 33
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Have I uncovered a PHP bug?
Hello, while making a pretty complex algebraic script in PHP, I encountered something strange.
Code:$tx = -.8; $r = sqrt(1.0 - $tx * $tx); var_dump($r); echo asin(.6 / $r) . " | "; $r = .6; var_dump($r); echo asin(.6 / $r);
float(0.6) NAN | float(0.6) 1.5707963267949
The operation is the same, the value of $r is the same, so why does asin give NAN in the first case?
It's annoying because I can't fix it.. does anyone have any ideas?
-
Jun 25, 2007, 12:13 #2
- Join Date
- Sep 2004
- Location
- Norway
- Posts
- 1,198
- Mentioned
- 4 Post(s)
- Tagged
- 1 Thread(s)
It is most probably due to php has a set decimal length for any float value you have worked on. Even if it does not show in the var_dump the variable $r has a bunch of decimal zero values attached. The number depends on the php.ini settings.
Try to restrict the value to a desired amount of decimals when using it. You can for example use: sprintf("%01.1f", $r)
Please note that you should change the decimal number depending on the accuracy needed in your algorithm.
-
Jun 25, 2007, 12:17 #3
- Join Date
- Apr 2005
- Location
- Helsingborg, Sweden
- Posts
- 168
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Interesting... Does sqrt return some other kind of variable than float or does php just have general problems with floats? Since .6/$r = 1 in this example echo(asin((float)((int) (.6 / $r)))) actually outputs the right answer, so if you type convert you can get away from this problem, but I guess that doesn't help you here though.
I can't help you solve this but I'm mighty interested myself in what causes this.
-
Jun 25, 2007, 12:22 #4
- Join Date
- Apr 2005
- Location
- Helsingborg, Sweden
- Posts
- 168
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by TheRedDevil
-
Jun 25, 2007, 12:22 #5
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
This is not a PHP issue with floating point precision, but a computer issue. Most decimal floating-point values simply cannot be accurately represented in binary. Fractions like 1/2 (0.5), 1/4 (0.25), 1/8 (0.125), etc (i.e. divide by a power of 2) can be, but others like 1/5 (0.2) cannot be, and still others can't even be accurately represented in decimal (e.g. 1/3 (0.33333...)), so you certainly can't expect binary to be able to handle them! When dealing with floating point numbers in any kind of computer system, you must be aware that the results will not always be what you expect, and will vary when you perform an identical operation on a different platform.
-
Jun 25, 2007, 12:35 #6
$r = sqrt(1.0 - rtrim(($tx * $tx),0));
will fix it.
Seems like the trailing 0's cause this bug.
-
Jun 25, 2007, 12:47 #7
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Actually, it's the mere act of converting to a string and back again that is resulting in the expected output:
PHP Code:$r = sqrt(1.0 - (string)($tx * $tx));
var_dump($r);
echo asin(.6 / $r) . "\n";
Again, floating-point on computers is really really tricky, and is the reason for several integer-based libraries that aim to emulate floating-point while avoiding rounding errors; most such libraries use integers to represent floating-point numbers by dividing the integer by e.g. 1,000 when displaying, and otherwise are performing nothing but slightly modified integer arithmetic. If anyone is interested in such uber-geeky stuff I can post a more detailed explanation of the cause of rounding errors (such as what we are seeing here), as well as how the integer-based floating point libraries work.
-
Jun 25, 2007, 13:10 #8
- Join Date
- Apr 2004
- Location
- germany
- Posts
- 4,324
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
-
Jun 25, 2007, 13:11 #9
- Join Date
- Sep 2004
- Location
- Norway
- Posts
- 1,198
- Mentioned
- 4 Post(s)
- Tagged
- 1 Thread(s)
Originally Posted by Borje
Yes, in the end this is a computer issue, as you say a floating point will never be accurate due to how the computers work. Thanks for noteing it, its not always Im able to get the wording properly when writing a reply
However keep in mind that the floating point system inside php is modified to be more "accurate" since it allow you do decide when to cut the decimals so the issue is not as "extreme" as it could have been.
Though for best compability it is best to force the decimal length yourself instead of depending on that you will be able to change the php setting.
For those of you who are still confused, since the var_dump displays the same number.
Functions like var_dump has already performed the rounding, while when you use the same variable in a math operation it is used as "it is" (can for example in this case be: 0.5999999999999998223643160599749535322189)
When you then limit the decimals, the value will automatically be rounded to that amount of decimals.
Originally Posted by kromey
-
Jun 25, 2007, 14:46 #10
- Join Date
- May 2007
- Posts
- 33
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Thanks for your help, it was an interesting read!
It seems that var_dump does indeed not output the complete value for $r, which causes the argument to the asin function to be larger than 1 and result in an error. It was easily fixed after I understood what was going on..
I've never seen a situation before where the rounding errors had such a big impact..
Again, thanks a lot!
Bas
-
Jun 25, 2007, 15:06 #11
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Heh, just play around with floating point arithmetic. Especially if you do a lot of operations on computed values. Each computation results in a little more error, and that builds up over time. Operations like square root compound a lot more error than e.g. multiplication or division. Keep it up and eventually you can end up being off by a whole lot more than a single bit way down at the end of the buffer.
@TheRedDevil
You're absolutely right that the stored value is almost always slightly less than the actual value, however in this case since we are subtracting the squared floating point (which is slightly less) from 1.0 (which can be represented with no error at all), we are in fact ending up slightly over the actual value, which is causing the arcsin function to return NAN because it is only valid from -1.0 to +1.0 and we're ever-so-slightly over +1.0. This is why the string conversion and explicit rounding both fix the problem in this instance.
-
Jun 25, 2007, 15:24 #12
- Join Date
- Sep 2004
- Location
- Norway
- Posts
- 1,198
- Mentioned
- 4 Post(s)
- Tagged
- 1 Thread(s)
kromey:
Ah, thanks for the explanation.
-
Jun 25, 2007, 15:53 #13
Bookmarks