在浮点运算中,如果两个数字具有相同的二进制表示形式,则对这些数字执行的任何操作的结果都应该相同,并且使用
==
进行相等比较应该按预期工作。
例如,如果 a 和 b 计算为
1.0/3.0
,它们在标准浮点系统中确实具有相同的二进制表示形式。因此,如下计算的 x
和 y
应该是相同的,并且该断言成立。
double a = 1.0/3.0;
double b = 1.0/3.0;
double x = a*a/Math::Pi;
double y = b*b/Math::Pi;
assert(x==y);
我的问题是,数字的符号会影响结果的准确性吗?以下内容永远正确吗?
double a = 1.0/3.0;
double b = -1.0/3.0;
double x = a*a/Math::Pi;
double y = -(-b*b/Math::Pi);
assert(x==y);
这个怎么样?这个说法成立吗?
double a = 1.0/3.0;
double b = 1.0/7.0;
double x = a-b;
double y = -(b-a);
assert(x==y);
我主要在 x86/x64 机器上工作。我认为 C/C++/ASM 应该有相同的行为,所以我标记了 C 和 C++。
TLDR: 理论上,符号会影响结果,但实际上不会。
浮点数的标准是IEEE-754。它指定符号以单独的位进行编码。这意味着,与使用 Two 的补码 的整数不同,范围在零周围完全对称。尾数和指数均不受符号影响。 因此符号不影响精度。
但是,IEEE-754 允许使用不同的舍入模式来处理尾数中的最低位。如果将舍入模式更改为正无穷大或负无穷大,这将影响结果。但从来没有人这样做过。并非每个硬件实现都支持这一点,例如 GPU 通常不支持。
严格来说,大多数语言的浮点类型不需要 IEEE-754。但是,您将很难找到不符合标准的广泛使用的系统。另请参阅为什么不使用基于二进制补码的浮点?
范围对称的期望也体现在很多代码中,包括标准库。例如,C++11 之前的 C++ 将
std::numeric_limits<float>::max()
表示最大的有限正值,将 std::numeric_limits<float>::min()
表示最小的非零、归一化的 positive 值。如果你想要最负的有限值,你必须否定 max()
。这仅在 C++11 中通过 std::numeric_limits<float>::lowest()
发生了变化,但对于 C 本身没有改变,它使用类似的 float.h
宏。