1

Something you would not expect but need to be aware of when you're dealing with floating point numbers in php

<?php


    $i = (32.87*100);

    echo $i;                 // outputs 3287
    echo (int) $i;           // outputs 3286 !!
    echo (int) (string) $i   // outputs 3287 

Internal representation of $i is something like 3286.9999999.

Why is the string representation of $i 3287 ?

2
  • If you change the order of the casts in your last example, you get 3286. echo (string) (int) $i; Commented Jan 17, 2019 at 15:52
  • 1
    A really impressive question, showing some research and background knowledge by pointing out the internal value. And all that as a very first question from a new contributer. Very nice! Commented Jan 17, 2019 at 20:42

2 Answers 2

3

Let's go through your code:

$i = (32.87*100);

Now $i is slightly less than 3287 as float as shown below:

echo sprintf('%.30f', $i) . PHP_EOL; //3286.999999999999545252649113535881

But when you print (echo) it, you'll get rounded value.

echo $i;                 // outputs 3287

And here we come to the trick - casting float to int means to simply cut off the part after dot, despite its .99999999(...) which is almost 1 (but it's not!). So the output is 3286.

echo (int) $i;           // outputs 3286 !!

Now, in the last example, you first cast float to string, which means exactly what you already did by doing echo $i; because whatever you print, internally PHP need to cast to string. So it's 3286.999999999999545252649113535881 casted to "3287" and then casted to 3287, and then printed.

echo (int) (string) $i   // outputs 3287;

To sum up, it's difference between the way float is casted to string and int.

EDIT Further explanation about "rounding"

Well it's not really rounding. I've made a mistake by saying that.

PHP uses 64 bit float (do called double), which in decimal representation has 14 digit precision.

As mentioned in PHP manual:

The size of a float is platform-dependent, although a maximum of approximately 1.8e308 with a precision of roughly 14 decimal digits is a common value (the 64 bit IEEE format).

That means, that a float can contain (for most of the time) a 14-digit number (in decimal) and it doesn't matter where the dot is placed.

Now, the most important thing:

Casting to string doesn't round the float number

Examples:

  • $a = 1.23456789012349 - the last 9 is 15th digit, so you'll get "rounded" float to 1.2345678901235
  • $a = 12345678901234.9 - same as above
  • $a = 1.99999999999999 - last 9 is 15th digit, so you'll get 2

And as a string it will be printed exactly as the float is, which means 14 digits precision. The "rounding" is at the moment when we create float variable's structure in memory.

The last example is what we're talking about in this topic.

Now, why I did that mistake and said about "rounding"? I misunderstood the result of echo sprintf('%.30f', $i). A saw many more digits and thought it's the real value of the float number.

But it's not.

As we know, 64-bit float has only 14 digits precision.

So where the result of sprintf comes from?

The answer is actually pretty easy.

We already know that it's not always possible to express a decimal number in binary system. So for example a simple 0.1 in float (binary representation) is just an approximation because the real binary representation would be infinitely long.

Now it works exactly the same when converting binary system to decimal. What can be expressed in binary (which means every float value), not always is possible to express in decimal.

So what sprintf('%.30f', $i) is to give the 30-digit precision approximation of converting the float number from binary to decimal system.

Thanks to @Quasimodo'sclone for asking in comment for being more precise about this. That made me go a little deeper in this topic.

Sign up to request clarification or add additional context in comments.

7 Comments

This explains more verbose what in the question is already said, but does not exactly answers the question "Why is the string representation of $i 3287 ?" Excactly in which case casting to a string will round? Consider (string) 1.999;
@Quasimodo'sclone here you go. See the update. Hope it explains it deep enough.
Deeper we hardly can go here.
I read this edited answer multiple times. This should be voted at least +50. And also the question should get high voting rates since it is not an every day's question and even shows some research pointing out the internal value. I do not understand why it got one downvote.
Thanks for this very extensive and clear explanation.
|
0

You're casting $i (3287) to a string and then to an int, so the result stays 3287. If you cast $i to an int you'll get 3286, and then if you cast it to a string you'll have what you want.

Try echo (string) (int) $i

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.