我正在使用格式为 %.2n 的 money_format,但得到的结果很奇怪。
我整理了示例代码,以便其他人可以自己测试。
<?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 />';
}
输出:
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
您会注意到第三个结果,203.775 显示为 $203.77 而不是 $203.78。
我对 money_format 的理解是否遗漏了什么?
phpfiddle 链接:http://phpfiddle.io/fiddle/1909516587
最佳答案
这是 floating point math operations
的常见问题
造成混淆的原因是 PHP 中的默认精度设置得太低,并且在从 var_dump
显示时显示 float 的舍入值。
来源:http://php.net/manual/en/language.types.float.php
要查看实际数字,您需要设置更高的精度或使用 printf
定义精度,这将显示更多来自 $fee
的浮点值。
ini_set('precision',32);
var_dump($fee);
//or
printf("%01.32f", $fee);
精度示例:https://3v4l.org/ue0Th
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
因此我们可以看到 number_format
的舍入不正确。当给定相同的十进制值时,我们得到一个向上舍入的数字和一个没有向上舍入的数字:https://3v4l.org/pgKDs
echo number_format(23.77499999999997, 2); //returns 23.78
echo number_format(2.77499999999997, 2); //returns 2.77
作为一般规则,尽量不要在涉及精度的地方使用 float 学,并支持尽可能低的面额(在一分钱的意义上),或者依赖 bcmath 函数并使用乘数来检索分数值. http://php.net/manual/en/book.bc.php
将计算更改为使用 bcmath 将解决 money_format
和 number_format
的问题:
BC 数学示例:https://3v4l.org/JEZds
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;
}
结果:
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
关于php - 使用 money_format 进行意外舍入,包括示例代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24829432/