C ++程序中的明显浮点错误
Blatant floating point error in C++ program
我正在为 double
变量分配双精度文字。
变量的值被截断,否则我无法理解为什么,例如差异 diff
是 0.0.
很抱歉 setprecision
处的代码重复,但我真的很生气。
#include <iostream>
#include <iomanip>
#include <cmath>
#include <limits>
int main()
{
long double d = 1300010000000000000144.5700788999;
long double d1 = 1300010000000000000000.0;
long double diff = d - d1; // shall be 144.5700788999!!!
long double d2 = 0.5700788999;
std::cout << "d = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << d << '\n';
std::cout << "d1 = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << d1 << '\n';
std::cout << "d - d1 = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << diff << '\n';
std::cout << "d2 = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << d2 << '\n';
}
这是输出:
d = 1300009999999999900000.0000000000000000
d1 = 1300009999999999900000.0000000000000000
d - d1 = 0.0000000000000000
d2 = 0.5700788999000001
我希望 diff
是 144.5700788999
但它是 0.0
那么,怎么处理呢? (Window 7 及更高版本,VS 2013)
...要使用两个双打,一个用于高值,一个用于低值?
就像,而不是使用 d
来使用 d1
和 d2
?
80 位 long double
(不确定其在 MSVS 中的大小)可以存储大约 18 位有效的十进制数字而不会丢失精度。 1300010000000000000144.5700788999
有 32 位有效小数位,不能完全按照 long double
.
存储
阅读 Number of Digits Required For Round-Trip Conversions 了解更多详情。
好吧,您将面对浮点数的狂野西部!不要相信任何人,不要抱太大希望,把手放在枪上。
事实是:浮点表示是一个拆分。给定数量的字节用于存储两个部分,尾数值和十次方(当然是简化描述,但它足以满足我们的需求)。一旦你有一个太多的值无法放入尾数,计算机应该怎么做?它必须将其余部分携带到另一部分字节(就像 Big Math 库所做的那样)或者只是四舍五入到最接近的可能值。让我展示一下:
d2 = 0.5700788999; // shows 0.5700788999000001
d2 = 1300010000000000000000.5700788999; // shows 1300009999999999934464.0000000000000000000
嘿,我在第二种情况下的小数部分在哪里?它消失了!报警!哦,等等,它只是不适合... 这就是 diff 给出零的原因:尾数太大以至于无法存储尾部(实际差异所在的位置)。只要其余数字相同,我们就有零差异。
经过仔细比较,你还发现了另外一件事:打印出来的值和赋值值很接近,但是还是有一点不同。这是因为尾数只是 2 的幂之和。因此,为了表示该值,计算机必须将分配的值四舍五入到最接近的 binary-compatible 一个。有时这是另一种痛苦,您不应该通过相等运算符比较 floating-point 数字,只需评估差异并将其与预期精度的预期增量进行比较。
我正在为 double
变量分配双精度文字。
变量的值被截断,否则我无法理解为什么,例如差异 diff
是 0.0.
很抱歉 setprecision
处的代码重复,但我真的很生气。
#include <iostream>
#include <iomanip>
#include <cmath>
#include <limits>
int main()
{
long double d = 1300010000000000000144.5700788999;
long double d1 = 1300010000000000000000.0;
long double diff = d - d1; // shall be 144.5700788999!!!
long double d2 = 0.5700788999;
std::cout << "d = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << d << '\n';
std::cout << "d1 = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << d1 << '\n';
std::cout << "d - d1 = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << diff << '\n';
std::cout << "d2 = " << std::fixed << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << d2 << '\n';
}
这是输出:
d = 1300009999999999900000.0000000000000000
d1 = 1300009999999999900000.0000000000000000
d - d1 = 0.0000000000000000
d2 = 0.5700788999000001
我希望 diff
是 144.5700788999
但它是 0.0
那么,怎么处理呢? (Window 7 及更高版本,VS 2013)
...要使用两个双打,一个用于高值,一个用于低值?
就像,而不是使用 d
来使用 d1
和 d2
?
80 位 long double
(不确定其在 MSVS 中的大小)可以存储大约 18 位有效的十进制数字而不会丢失精度。 1300010000000000000144.5700788999
有 32 位有效小数位,不能完全按照 long double
.
阅读 Number of Digits Required For Round-Trip Conversions 了解更多详情。
好吧,您将面对浮点数的狂野西部!不要相信任何人,不要抱太大希望,把手放在枪上。
事实是:浮点表示是一个拆分。给定数量的字节用于存储两个部分,尾数值和十次方(当然是简化描述,但它足以满足我们的需求)。一旦你有一个太多的值无法放入尾数,计算机应该怎么做?它必须将其余部分携带到另一部分字节(就像 Big Math 库所做的那样)或者只是四舍五入到最接近的可能值。让我展示一下:
d2 = 0.5700788999; // shows 0.5700788999000001
d2 = 1300010000000000000000.5700788999; // shows 1300009999999999934464.0000000000000000000
嘿,我在第二种情况下的小数部分在哪里?它消失了!报警!哦,等等,它只是不适合... 这就是 diff 给出零的原因:尾数太大以至于无法存储尾部(实际差异所在的位置)。只要其余数字相同,我们就有零差异。
经过仔细比较,你还发现了另外一件事:打印出来的值和赋值值很接近,但是还是有一点不同。这是因为尾数只是 2 的幂之和。因此,为了表示该值,计算机必须将分配的值四舍五入到最接近的 binary-compatible 一个。有时这是另一种痛苦,您不应该通过相等运算符比较 floating-point 数字,只需评估差异并将其与预期精度的预期增量进行比较。