skip to Main Content

Observe the following case

php -r 'echo 4.60*100, " ", (int)(4.60*100), PHP_EOL;'

Surprisingly (to me) it prints this:

460 459

I understand that decimal 4.60 cannot be represented as finite binary number, but why

  • string representation of float 4.60 prints exactly as 4.60 rather than 4.59…99?
  • integer cast from float (4.60*100) ends up 459 rather than 460?

2

Answers


  1. Browsing the PHP documentation, I do not see a strict specification of the floating-point format or of the formatting for converting floating-point values to strings. So this answer is for illustrating the general principles involved, not a strict specification based on the documentation.

    The IEEE-754 “double precision” (binary64) format is commonly used for floating-point. In this format, the closest representable value to 4.6 is 4.5999999999999996447286321199499070644378662109375. Multiplying that by 100 in the yields 459.99999999999994315658113919198513031005859375 in the binary64 format.

    Executing echo forces a conversion from the floating-point type to a string. I found nothing in the documentation specifying how this conversion is performed, but some experiments suggest it is, in at least one implementation, converted as if by the C format %.14g, meaning to use 14 significant digits. Rounding 459.99999999999994315658113919198513031005859375 to 14 significant digits yields 460 (“460.00000000000”), so that is the displayed output.

    Since PHP is not specified strictly, you may see different outputs in different implementations.

    Login or Signup to reply.
  2. The accurate value of 4.60*100 is about 459.999999999999943....

    (int)(4.60*100) simply truncates the fraction part, so the result is 459.

    echo and var_dump use sprintf with different precisions to convert the number to string, e.g:

    $test = 4.60*100;
    echo sprintf('%.14G', $test), PHP_EOL; // echo
    echo sprintf('%.17G', $test), PHP_EOL; // var_dump
    

    Before PHP8, echo and var_dump behave the same, but things got changed since this commit a939805:

    var_dump() is debugging functionality, so it should print
    floating-point numbers accurately. We do this by switching to
    serialize_precision, which (by default) will print with as much
    precision as necessary to preserve the exact value of the float.

    This also affects debug_zval_dump().

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search