如何在一个区间内循环遍历双精度的每个值?

时间:2014-04-20 08:22:38

标签: c++ double epsilon

我读了here并得出结论,double的最小值(绝对值)是1.7e-308,但我的代码永远循环:

for (double d(-1.0); d <= 1.0; d+=1.7e-308)
{
}

编辑:我希望以尽可能小的增量从-1.0循环到1.0。

3 个答案:

答案 0 :(得分:9)

在C ++ 11 / C99中,您可以使用nextafter来获取下一个可表示的数字。

#include <cmath>

for (double d(-1.0); d <= 1.0; d = std::nextafter(d, 1.0) )
{
}

此外,如果我正确地进行了数学计算,则在-1和1之间有大约10 ^ 18个可表示的值。

合理假设每秒10 ^ 6个周期,执行此计算需要32000年。

答案 1 :(得分:3)

  

TL; DR:随着数字变大,计算机能够以浮点数存储的数字之间的差距变大。您的最小数量仅在数字为零时有效。在某些时候,尝试添加这么少的数字并没有任何作用,因为它不能代表如此小的数字变化。

这是因为每个可表示的浮点数之间的差距随着数量的增加而变大。

1与最接近的可表示小于1之间的差值约为1.11×10 -16 ,远大于您尝试添加的值。

所发生的事情是,double存储有一组位用于数字的符号,另一个用于表示数字本身(作为无符号整数)和乘数作为指数2。

当你接近于零时,指数非常小,实际存储数量的一个变化小于你的指数要大得多。

例如,(4 - 3)×2 99 显然会大于(4 - 3)×2 -99 ,这基本上是基本的发生。

当你开始添加时,你会发现最终,添加1.7×10 -308 什么都不做,因为你的当前值是最接近的可表示值,它继续保持不变。无限循环。

请注意,仅当舍入模式设置为roundTiesToEven或roundTiesToAway时,它们才会舍入到最接近的可表示值,除非它们同样接近。

如果您事先用std::fesetround FE_UPWARD调用as Marc Glisse noted手动将舍入模式设置为roundTowardPositive,如果添加任何正数(零除外),它将继续增加。我认为roundTowardNegative和roundTowardZero是不言自明的。

顺便说一下,即使你用1.11×10 -16 替换它,你仍然需要在完成之前通过18 014 398 509 481 984(18千万亿)迭代。如果你实际循环遍历-1和1之间的每个值,那就是9 223 372 036 854 775 809(9 quintillion)迭代。祝你好运。

答案 2 :(得分:0)

我认为双倍的最小值是1.7e-308,它可能更像-1.7 e308,1.7e-308是一个非常小的数字,但不是负数无穷大的数字。这是一个非常小的数字,接近0。