24151.40 - 31891.10 = -7739.699999999997
我从 MySQL 表中获取这两个数字,类型为 decimal(14,2) 24151.40 31891.10 它完全按照上述方式保存,它的回声与 PHP 中的完全一样。但是从第一个值中减去第二个值的那一刻,我得到一个数字 -7739.699999999997 而不是 -7,739.7。为什么要额外的精度?它来自哪里?
一加一等于二,对吧?0.2 加上 1.4 乘以 10 怎么样?那等于16,对吧?如果您使用 PHP(或大多数其他编程语言)进行数学运算,则不会:
echo floor((0.2 + 1.4) * 10); // Should be 16. But it's 15!
这是由于浮点数是如何在内部处理的。它们以固定的小数位数表示,并且可能导致数字加起来不像您期望的那样。在内部,我们的 0.2 加 1.4 乘以 10 示例计算大约为 15.9999999998 左右。当处理不需要像百分比这样精确的数字时,这种数学方法很好。但是,当处理货币精度问题时,这里或那里丢失的一分钱或一美元很快就会加起来,没有人喜欢在任何丢失的钱中亏本。
BC 数学解决方案
幸运的是,PHP 提供了BC Math 扩展,它是“用于任意精度数学的 PHP 提供了二进制计算器,它支持任何大小和精度的数字,以字符串表示。” 换句话说,您可以使用此扩展对货币价值进行精确的数学运算。BC Math 扩展包含允许您精确执行最常见操作的函数,包括加法、减法、乘法和除法。
一个更好的例子
这是与上面相同的示例,但使用 bcadd() 函数为我们进行数学运算。它需要三个参数。前两个是我们希望添加的值,第三个是我们希望精确到的小数位数。由于我们使用的是金钱,我们将精度设置为小数点后两位。
echo floor(bcadd('0.2', '1.4', 2) * 10); // It's 16 like we would expect it to be.
PHP 不像 MySQL 那样具有十进制类型,它使用浮点数;浮点数因不准确而臭名昭著。
要解决这个问题,请查看number_format
,例如:
echo number_format(24151.40 - 31891.10, 2, '.', '');
为了更准确的数字操作,您还可以查看 PHP 的数学扩展:
这与一般浮点/双精度率有关,这在科学上与 1.FRACTAL * 2^exponential 幂有关。由于前缀为 1,因此在技术上没有零这样的东西,您可以获得的最接近 0 的值是 1.0 * 2 ^ -127,即 .000000[127 0s]00001
通过将您的答案四舍五入到一定的精度,舍入因子将为您提供更精确的答案
http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_round