3

I have a simple piece of code as below.

$amount = 447274.44882;
$rate = 0.00001;

echo floatNumber(bcmul($amount, $rate, 8), 8);

This outputs 0.00000000 when it should be 4.47274449. If I change the rate to 0.0001 then it outputs the correct number, anything higher than 4 decimals and it reports 0.

Am I doing something wrong or is this a known limitation or something? Seems quite a big one if that's the case.

Linkandzelda
  • 611
  • 14
  • 24

1 Answers1

4

If you cast 0.00001 to string using the default settings (and that's what will happen if you feed bcmul with floats since it expects strings) you'll get this:

var_dump( (string)0.00001 );
string(6) "1.0E-5"

It isn't clearly documented but bcmath functions apparently return cast to zero when faced to invalid input:

var_dump( bcadd('Hello', 'world!', 8) );
var_dump( bcadd('33', 'Foo', 8) );
var_dump( bcdiv('33', 'Foo', 8) );
string(10) "0.00000000"
string(11) "33.00000000"
Warning: bcdiv(): Division by zero
NULL

The whole idea of arbitrary precision libraries is to overcome the limitations of base 2 arithmetic and fixed size storage. Thus you'd need this:

var_dump( bcmul('447274.44882', '0.00001', 8) );
string(10) "4.47274448"

This is great to do math with 100-digit numbers but not particularly useful for simple rounding. In fact, the extension doesn't round at all—it merely truncates:

var_dump( bcmul('20.01', '1.444', 3) );
var_dump( bcmul('20.01', '1.444', 2) );
var_dump( bcmul('20.01', '1.444', 1) );
var_dump( bcmul('20.01', '1.444', 0) );
string(6) "28.894"
string(5) "28.89"
string(4) "28.8"
string(2) "28"
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • This answer is very elaborate, thanks. I still don't know if using BCmul even when casting between strings will result in inaccurate calculations with large 8 digit floating point numbers. – Linkandzelda Feb 06 '14 at 17:28
  • You *can* get inaccurate calculations due to how IEEE floating point precision works. But most of the times it happens in real life it's because of rounding in the wrong place (or not rounding at all). And you can always use *integers* (e.g., cents instead of euros). – Álvaro González Feb 06 '14 at 17:39