减法时PHP浮点计算错误 [英] PHP float calculation error when subtracting

查看:30
本文介绍了减法时PHP浮点计算错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个很奇怪的问题.如果我减去 2 个浮点变量,其中一个是数学运算的结果,我会得到一个错误的值.

I have a very strange issue. If I subtract 2 float vars where one is the result of a mathematical operation I get a wrong value.

示例:

var_dump($remaining);
var_dump($this->hours_sub['personal']);
echo $remaining-$this->hours_sub['personal'];

这是输出:

float 5.4
float 1.4
5.3290705182008E-15

5.4-1.4 应该是 4如果我将这两个值相加,结果是正确的.

5.4-1.4 should be 4 If I add the two values the result is correct.

我的错误在哪里?这不可能是一个四舍五入的问题.

Where is my mistake? It can not be a rounding issue.

推荐答案

如果仍然有人遇到类似的问题,其中浮点数减法导致错误或奇怪的值.下面我将更详细地解释这个问题.

If still somebody hits this page with similar problems where floating number subtraction causes error or strange values. Below I will explain this problem with a bit more details.

它与 PHP 没有直接关系,也不是错误.但是,每个程序员都应该意识到这个问题.

It is not directly related to PHP and it is not a bug. However, every programmer should be aware of this issue.

这个问题在 20 年前甚至夺去了许多人的生命.

This problem even took many lives two decades ago.

1991 年 2 月 25 日,MIM-104 爱国者导弹电池的浮点运算不正确(称为舍入误差)使其无法在沙特阿拉伯宰赫兰拦截来袭的飞毛腿导弹,造成 28 名士兵死亡,近 100 名军人受伤美国陆军第 14 军需分队.

On 25 February 1991 an incorrect floating-point arithmetic (called rounding error) in a MIM-104 Patriot missile battery prevented it from intercepting an incoming Scud missile in Dhahran, Saudi Arabia, killing 28 soldiers and injuring near 100 servicemen from the U.S. Army's 14th Quartermaster Detachment.

但是为什么会这样呢?

原因是浮点值表示有限的精度.所以,一个值可能任何处理后都没有相同的字符串表示(切掉).它也是包括在脚本中直接写入浮点值无需任何数学运算即可打印.

The reason is that floating point values represent a limited precision. So, a value might not have the same string representation after any processing (chopped off). It also includes writing a floating point value in your script and directly printing it without any mathematical operations.

举个简单的例子:

$a = '36';
$b = '-35.99';
echo ($a + $b);

您希望它打印 0.01,对吗?但它会打印一个非常奇怪的答案,如 0.009999999999998

You would expect it to print 0.01, right? But it will print a very strange answer like 0.009999999999998

与其他数字一样,浮点数 double 或 float 作为 0 和 1 的字符串存储在内存中.浮点数与整数的不同之处在于我们在查看 0 和 1 时如何解释它们.它们的存储方式有很多标准.

Like other numbers, floating point numbers double or float is stored in memory as a string of 0's and 1's. How floating point differs from integer is in how we interpret the 0's and 1's when we want to look at them. There are many standards how they are stored.

浮点数通常作为符号位、指数字段和有效数或尾数从左到右打包到计算机数据中......

Floating-point numbers are typically packed into a computer datum as the sign bit, the exponent field, and the significand or mantissa, from left to right....

由于空间不足,十进制数字不能很好地用二进制表示.所以,你不能完全把 1/3 表达成 0.3333333...,对吧?出于同样的原因,我们不能将 0.01 表示为二进制浮点数.1/1000.00000010100011110101110000..... 带有重复的 10100011110101110000.

Decimal numbers are not well represented in binary due to lack of enough space. So, you can't express 1/3 exactly as it's 0.3333333..., right? Why we can't represent 0.01 as a binary float number is for the same reason. 1/100 is 0.00000010100011110101110000..... with a repeating 10100011110101110000.

如果 0.01 以二进制的 01000111101011100001010 的简化和系统截断形式保存,当它转换回十进制时,它会读成 0.0099999.... 取决于系统(64 位计算机将提供比 32 位更好的精度).在这种情况下,操作系统决定是按其所见打印还是如何以更易读的方式打印.因此,他们想要如何表示它取决于机器.但是可以通过不同的方法在语言层面进行保护.

If 0.01 is kept in simplified and system-truncated form of 01000111101011100001010 in binary, when it is translated back to decimal, it would be read like 0.0099999.... depending on system (64bit computers will give you much better precision than 32-bits). Operating system decides in this case whether to print it as it sees or how to make it in more human-readable way. So, it is machine-dependent how they want to represent it. But it can be protected in language level with different methods.

如果您使用

echo number_format(0.009999999999998, 2);

它会打印0.01.

这是因为在这种情况下,您会指示应该如何读取以及您需要的精度.

It is because in this case you instruct how it should be read and how precision you require.

注意 number_format() 不是唯一的函数,还有一些其他的函数和方式可以用来告诉编程语言关于精度期望.

Note number_format() is not the only function, a few other functions and ways can be used to tell the programming language about the precision expectation.

参考资料:
https://sdqweb.ipd.kit.edu/publications/pdfs/saglam2016a.pdf
https://en.wikipedia.org/wiki/Round-off_error

这篇关于减法时PHP浮点计算错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆