5

Can somebody explain this ?

echo ceil( 20.7 * 100 ); // returns 2070
echo ceil( 2070 );       // returns 2070

all OK and logical, but

echo ceil( 40.7 * 100 ); // returns 4071
echo ceil( 4070 );       // returns 4070

not OK and not logical...

Why is this difference ?

Thanks

lorandd
  • 164
  • 3
  • 10
  • 1
    Ever heard of IEEE 754? It's not impossible that the IEEE representation of `4070` actually is `4070.0000...1`, which `ceil` quite rightfully rounds up to `4071`. – Romain Nov 25 '11 at 14:36
  • 1
    Use `round` instead as you can set precision and mode. – nullpotent Nov 25 '11 at 14:37
  • [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – codaddict Nov 25 '11 at 14:47

5 Answers5

7

The wonderful world of floating point numbers:

printf("%.18f\n", 40.7*100);

//prints 4070.000000000000454747

printf("%.18f\n", 20.7*100);

//prints 2070.000000000000000000

In short: floating point numbers cannot represent all rational numbers exactly. In particular, neither 407/10 nor 207/10 can be represented exactly, and so the result of integer conversion always has an uncertainty of one unit.

The only rational numbers which can be represented exactly as binary floating point numbers are of the form "small odd integer times power of two", or in other words, those which have a small binary expansion.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
3

Floating point errors. 40.7 cannot be represented exactly in a float. It'll be something like 40.700000001 or whatever. When you * 100 and ceil it, it rounds up to 4071.

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • and when ceilling 20.7 * 100 it is 2070... and when multiplying with 40 it is not 4070, why ? it should work in the same way.. – lorandd Nov 25 '11 at 14:38
  • because floating point errors are never "it's always larger" or "it's always smaller". The actual internal representation will be EITHER larger or smaller. So for 40.7 it's slightly larger, and for 20.7 it's slightly smaller. – Marc B Nov 25 '11 at 14:39
  • `sprintf('%0.20f', 20.7) -> 20.69999999999999928946`. For 40.7, it's `40.70000000000000284217` – Marc B Nov 25 '11 at 14:41
  • @MarcB: THough note that a 64-bit IEEE float doesn't actually have 20 decimal digits of precision. It's 16 to 17 max, I believe (53 bits). – Kerrek SB Nov 25 '11 at 15:18
1

Use arbitrary precision library bcmath e.g.:

ceil(bcmul(40.7, 100)); // 4070
lubosdz
  • 4,210
  • 2
  • 29
  • 43
0

You can overcome your problem with something like this:

$result = 40.7 * 100;
$result = (string) $result;
echo ceil($result);
Namas
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 13 '22 at 08:41
0

Floating point numbers issue... you can overcome your problem with something like this:

echo ceil( (int) (40.7 * 100) );
Shomz
  • 37,421
  • 4
  • 57
  • 85