SitePoint Sponsor

User Tag List

Results 1 to 13 of 13
  1. #1
    SitePoint Enthusiast
    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);
    Output is:
    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?

  2. #2
    SitePoint Wizard TheRedDevil's Avatar
    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.

  3. #3
    SitePoint Zealot
    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.

  4. #4
    SitePoint Zealot
    Join Date
    Apr 2005
    Location
    Helsingborg, Sweden
    Posts
    168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TheRedDevil
    It is most probably due to php has a set decimal length for any float value you have worked on.
    Then why does it work with one float but not the other one returned from the sqrt();?

  5. #5
    Worship the Krome kromey's Avatar
    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.
    PHP questions? RTFM
    MySQL questions? RTFM

  6. #6
    SitePoint Wizard wonshikee's Avatar
    Join Date
    Jan 2007
    Posts
    1,223
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    $r = sqrt(1.0 - rtrim(($tx * $tx),0));

    will fix it.

    Seems like the trailing 0's cause this bug.

  7. #7
    Worship the Krome kromey's Avatar
    Join Date
    Sep 2006
    Location
    Fairbanks, AK
    Posts
    1,621
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wonshikee View Post
    $r = sqrt(1.0 - rtrim(($tx * $tx),0));

    will fix it.

    Seems like the trailing 0's cause this bug.
    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"
    The most likely cause is an errant 1 way way waaaaaay down toward the end of the internal floating-point buffer. By converting to a string and back again you are essentially forcing a rounding error that, in this case, is fixing another rounding error. Another fix is to round the result of squaring $tx to e.g. 5 places; this latter solution is most often the fix used to solve such rounding errors.

    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.
    PHP questions? RTFM
    MySQL questions? RTFM

  8. #8
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by basvanmeurs View Post
    The operation is the same, the value of $r is the same, so why does asin give NAN in the first case?
    Actually, two $r's are not the same. Try "echo serialize($r)" instead of "var_dump".

  9. #9
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,198
    Mentioned
    4 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Borje
    Then why does it work with one float but not the other one returned from the sqrt();?
    Because the last one has not been modified.

    Quote Originally Posted by kromey View Post
    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.
    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.

    Quote Originally Posted by kromey
    The most likely cause is an errant 1 way way waaaaaay down toward the end of the internal floating-point buffer. By converting to a string and back again you are essentially forcing a rounding error that, in this case, is fixing another rounding error.
    The stored value is usally always slightly less than the value it should be (like the example above), in my experience its seldom higher. I.e. the floating point is usally always rounded up.

  10. #10
    SitePoint Enthusiast
    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

  11. #11
    Worship the Krome kromey's Avatar
    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.
    PHP questions? RTFM
    MySQL questions? RTFM

  12. #12
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,198
    Mentioned
    4 Post(s)
    Tagged
    1 Thread(s)
    kromey:
    Ah, thanks for the explanation.

  13. #13
    Worship the Krome kromey's Avatar
    Join Date
    Sep 2006
    Location
    Fairbanks, AK
    Posts
    1,621
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah, I left that critical part of my analysis out of my conclusion. My bad. Hope I've now cleared up any confusion I've caused.
    PHP questions? RTFM
    MySQL questions? RTFM


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •