2

I'm using a Money class that requires all currency to be in int in order to instantiate it. What's happened is I've run across a specific number (I've tried with a variety of other numbers and everything works as expected) that when type cast from float to int, has a different value.

I might expect this from a very high precision number due to the nature of floating point numbers in general, but this is not making any sense, here's how I can reproduce the error...

$value = (float)9.78;
var_dump($value );
$prec = (int)(100);
var_dump($prec);
$value = $value * $prec;
var_dump($value);
var_dump((int)($value));

... which produces the following output ...

float(9.78)  /* $value as a float */
int(100)     /* $prec as an int */
float(978)   /* $value * $prec as a float, all going well... */
int(977)     /* $value type cast to an int ???????? */

... what the hell is going on here? Why is $value type cast to an int in this scenario coming up with a different value?


EDIT: The reason I'm not accepting this as a duplicate is due to the answer I needed not being present in the other thread. Here it is: I had to apply round() like so...

$dec_precision = strlen((string)($prec)-1);
$value = round($value * $prec, $dec_precision);

Hope that helps someone!

oucil
  • 4,211
  • 2
  • 37
  • 53

1 Answers1

2

PHP uses exponential representations of floating numbers, so it is not precise. Read docs:

Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.

Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9999999999999991118....

UPDv1:

Note PHP math extensions: BCMath and GMP.

BlitZ
  • 12,038
  • 3
  • 49
  • 68
  • Man, my understanding of float was way off, I was under the (uneducated) assumption that the epsilons wouldn't be an issue until you were actually dealing with high precision numbers, but in reality, even 0.1 according to the docs is susceptible. Guess I just needed to RTFM just one more time. – oucil Sep 26 '15 at 07:36