Question

I just "fixed" a bug in the following lines of PHP:

        $value = 1091.09; // the failing test case
        $prop = sprintf('%013d', $value * 100);

by adding those lines:

        $cents = $value * 100; // $cents is now 109109
        $cents = round($cents, 2); // $cents is still 109109
        $prop = sprintf('%013d', $cents);

The result for the former block is "0000000109108", while that of the second one is "0000000109109", which is correct.

Note that I added both lines to be able to view each step in the debugger separately. It also works if I skip the first line, thus writing:

        $cents = round($value * 100, 2); // $cents is now 109109
        $prop = sprintf('%013d', $cents);

So, apparently, the round() function does something invisible to the value that makes it work differently with sprintf(). What is it?

Were this a proper language, I would probably know by looking at the data types. Here, I don't even know what they are.

Était-ce utile?

La solution

it maybe not as simple...

php > $value = 1091.09; print sprintf('%f', $value);
1091.090000
php > $value = 1091.09; print sprintf('%f', $value*100);
109109.000000
php > $value = 1091.09; print sprintf('%f', (int)($value*100));
109108.000000

One would expect to see all these '999999' in first two results.

Additionally:

php > $value = 0.09; print sprintf('%f', $value);
0.090000
php > $value = 0.09; print sprintf('%f', $value*100);
9.000000
php > $value = 0.09; print sprintf('%f', (int)($value*100));
9.000000

(NOTE: same result of 0.009 and 0.9)

Bottom line, @Mihai is probably right in his answer, but it seems that these "approximate" float numbers (with infinite number of 99999's) exist some where deep inside bowls of floating point arithmetic unit and influence PHP level calculations only in some very particular cases.

Autres conseils

To the point:

The initial datatype is float64.

After multiplication by 100 it's still a float.

Printing (with echo/print_r/var_dump) the value does not print the absolute exact value (109108.9999999999999); it prints the approximated float value 109109 because floats are inherently approximations not exact values.

Printing it as an int using sprintf triggers a type conversion to int which truncates the value of 109108.9999999999999 to 109108.

Hence your issue.


As a side note:

The basic types supported by PHP are constructed using a handler variable and a reference counted container. Other than that they are pretty much low level stuff.

There's no reason to think they are "more complex" than they appear. I get thinking that about a complex object but not about a scalar datatype.

Also a large confusion factor is (implicit) type-conversion and all the type conversion rules (including everything that can pass as boolean false).

Ex.1:

  -1 == false // true
null == false // true
  -1 != null  // false

Ex.2:

0  == false // true
0 === false // false
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top