-1

This question is best posed as a block of code. Basically I need to know why $float2 is being rounded to 1 whenever it is cast to a string with (string) and also need a way to work around this problem. I'm able to cast other floats to a string without any issue, but this one case where rounding is happening is causing major headaches.

   $float1 = 0.99999999999999;
   $float2 = 0.999999999999999;
   $float3 = 0.00000000000001;
   $float4 = 0.000000000000001;
   echo $str1 = float_to_string($float1);
   echo ' type: '.gettype($str1)."\n"; //Echos:  0.99999999999999 type: string


   echo $str2 = float_to_string($float2);
   echo ' type: '.gettype($str2)."\n"; //Echos: 1 type: string

   echo $str3 = float_to_string($float3);
   echo ' type: '.gettype($str3)."\n"; //Echos: 0.00000000000001 type: string

   echo $str4 = float_to_string($float4);
   echo ' type: '.gettype($str4)."\n"; //Echos: 0.000000000000001 type: string


   if ($float2 != 1)
   {
      echo "No rounding done in if clause so this means that (string) is doing the rounding.\n";
   }

function float_to_string($float)
{
   $parts = explode('E', $float);
   if(count($parts) === 2)
   {
      $exp = abs(end($parts)) + strlen($parts[0]);
      $decimal = number_format($float, $exp);  //This converts it to a string
      echo "Returning with number_format\n";
      return rtrim($decimal, '.0');
   }
   else
   {
      echo "Returning without number_format\n";
      return (string)$float; //Why is this rounding 0.999999999999999 to 1?!  I thought only number_format did rounding.
   }
}
totalnoob
  • 31
  • 6
  • 2
    https://stackoverflow.com/questions/588004/is-floating-point-math-broken – John Conde Apr 12 '19 at 18:57
  • No it's not already 1 because the if clause I have in there proves that. It prints "No rounding done yet" which means it doesnt == 1 yet. This means it IS the (string) that does the rounding. – totalnoob Apr 12 '19 at 20:22

1 Answers1

2

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).

https://www.php.net/manual/en/language.types.float.php

The precision on PHP is 14 digits, your first one is 14 digits, the second is 15 and exceeds the precision so it gets rounded.

Also I believe you can set the precision in your ini to exceed 14 digits, but because of how floating point numbers work your math may end up inaccurate.

Lulceltech
  • 1,662
  • 11
  • 22
  • I dont think this is correct because if it were, the if clause would not print "No rounding done". So there is something else going on when the same value is cast as a (string). Somewhere in the cast it's doing the rounding. So if (string) cant be used, then something else can be used maybe? I have tried floor(), bcadd(), strval(), and floatval(), all of which result in rounding to 1, yet if clause works perfectly. – totalnoob Apr 12 '19 at 19:21
  • If you're ever storing a floating point in a float before you cast it to a string it will have already been rounded. I.e $a = 0.900000000000009 will already have been rounded to 1 as soon as I stored it. Try turning up your precision in the ini or using ini_set('precision', 20); and you'll see – Lulceltech Apr 12 '19 at 19:23
  • How can it already have been rounded to 1 and then NOT equal 1? I dont get what you're saying. I have an if clause to check to see if it's been rounded yet or not, and it hasnt from what my code execution displays. Yet when I case it, it DOES do rounding. I get that floats are being rounded, but it seems to be taking place in (string) and not on the float itself which to me means that it should still be able to be cast to a string without further rounding. If what you're saying is true, that would mean it's rounded TWICE and thats clearly not the case here. – totalnoob Apr 12 '19 at 20:03
  • So my question of why is (string) doing rounding still stands, why is the rounding taking place on the cast through (string). If thats the case, then maybe some other method can be used where it will not do any further rounding. – totalnoob Apr 12 '19 at 20:06
  • In IEEE 754 the closest 64 bit binary float to 0.999999999999999 is 0.99999999999999900079927783735911361873149871826171875, supporting the idea that the rounding to 1 is being done by the cast to string, not the original assignment. – Patricia Shanahan Apr 12 '19 at 23:46