PHP cast float to string is different from cast to int

前端 未结 2 1373
执念已碎
执念已碎 2021-01-07 09:16

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



        
相关标签:
2条回答
  • 2021-01-07 10:10

    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

    0 讨论(0)
  • 2021-01-07 10:14

    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.

    0 讨论(0)
提交回复
热议问题