Reputation: 1385
I'm using money_format, with format of %.2n but am getting a weird result.
I've put together sample code, so others may test for themselves.
<?php
setlocale(LC_MONETARY, 'en_US');
$deal = array(
array(
'amt' => 1350,
'rate' => .75,
'lod' => 47
),
array(
'amt' => 990,
'rate' => .75,
'lod' => 27
),
array(
'amt' => 4180,
'rate' => .75,
'lod' => 65
),
array(
'amt' => 2370,
'rate' => .75,
'lod' => 26
)
);
foreach ($deal as $value) {
$fee = (($value['amt'] / 1000) * $value['rate']) * $value['lod'];
var_dump($fee);
echo '<br />money_format: ', money_format('%.2n', $fee), '<br />number_format: ', number_format($fee, 2), '<br /><br />';
}
Output:
float(47.5875)
money_format: $47.59
number_format: 47.59
float(20.0475)
money_format: $20.05
number_format: 20.05
float(203.775)
money_format: $203.77
number_format: 203.78
float(46.215)
money_format: $46.22
number_format: 46.22
You'll notice for the third result, 203.775 appears as $203.77 as opposed to $203.78.
Am I missing something in my understanding of money_format?
phpfiddle link: http://phpfiddle.io/fiddle/1909516587
Upvotes: 3
Views: 932
Reputation: 18416
This is a common issue with floating point math operations
The reason for the confusion is that by default precision in PHP is set too low and is displaying a rounded value for the floating point when displayed from var_dump
.
Source: http://php.net/manual/en/language.types.float.php
To see the actual number you will need to set a higher precision or define the precision using printf
, which will display more of the float value from $fee
.
ini_set('precision',32);
var_dump($fee);
//or
printf("%01.32f", $fee);
float(47.587500000000005684341886080801)
money_format: 47.59
number_format: 47.59
float(20.04749999999999943156581139192)
money_format: 20.05
number_format: 20.05
float(203.77499999999997726263245567679)
money_format: 203.77
number_format: 203.78
float(46.215000000000003410605131648481)
money_format: 46.22
number_format: 46.22
As a result we can see that number_format
is incorrectly rounding. When given the same decimal value, we get one number that is rounded up and one that is not: https://3v4l.org/pgKDs
echo number_format(23.77499999999997, 2); //returns 23.78
echo number_format(2.77499999999997, 2); //returns 2.77
As a general rule, try to not use floating point math where precision is involved and favoring the lowest possible denomination (in the sense of money a cent), or rely on the bcmath functions and using a multiplier to retrieve the fractional value. http://php.net/manual/en/book.bc.php
Changing the calculations to use bcmath will resolve the issue with both money_format
and number_format
:
bcscale(4);
foreach ($deal as $value) {
$fee = (float) bcmul(bcmul(bcdiv($value['amt'], 1000), $value['rate']), $value['lod']);
echo 'money_format: ' . money_format('%.2n', $fee) . PHP_EOL;
echo 'number_format: ' . number_format($fee, 2) . PHP_EOL . PHP_EOL;
}
Results:
string(7) "47.5875"
money_format: 47.59
number_format: 47.59
string(7) "20.0475"
money_format: 20.05
number_format: 20.05
string(8) "203.7750"
money_format: 203.78
number_format: 203.78
string(7) "46.2150"
money_format: 46.22
number_format: 46.22
Upvotes: 5
Reputation: 1084
A solution to international support would be something like the following:
<?php
setlocale(LC_MONETARY, 'en_US');
function _money_format($number, $decimals=2){
$number = number_format($number, $decimals);
$local_settings = localeconv();
$currency_symbol = $local_settings['currency_symbol'];
return $currency_symbol . $number;
}
echo _money_format('23.4567');
?>
Still getting the currency symbol + using number_format which should get what you need.
Demo here: http://phpfiddle.io/fiddle/772115651
Upvotes: 0