今天,我在看一些c++代码(别人写的),发现了这一部分:

double someValue = ...
if (someValue <  std::numeric_limits<double>::epsilon() && 
    someValue > -std::numeric_limits<double>::epsilon()) {
  someValue = 0.0;
}

我在想这到底说得通不合理。

epsilon()的文档说:

该函数返回1与可[用双精度符号]表示的大于1的最小值之间的差值。

这是否也适用于0,即()的最小值大于0?或者有没有0到0 +之间的数可以用双精度数表示?

如果不是,那么比较是不是等同于someValue == 0.0?


当前回答

假设64位IEEE双精度,则有52位尾数和11位指数。让我们把它分解一下:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1

大于1的最小可表示数:

1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52

因此:

epsilon = (1 + 2^-52) - 1 = 2^-52

在0和之间有数字吗?很多……例如,最小正可表示(正常)数为:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022

事实上,在0和之间有(1022 - 52 + 1)×2^52 = 4372995238176751616个数字,这是所有正可表示数字的47%…

其他回答

我认为这取决于你电脑的精度。 看一下这张表:你可以看到,如果用double表示,但你的精度更高,比较并不等于

someValue == 0.0

不管怎样,这是个好问题!

你不能把这个应用到0,因为有尾数和指数部分。 由于指数可以存储很小的数,小于, 但是当你尝试做一些类似(1.0 -“非常小的数字”)的事情时,你会得到1.0。 Epsilon不是值的指示器,而是值精度的指示器,值精度是尾数。 它显示了我们可以存储多少个正确的十进制数字。

Also, a good reason for having such a function is to remove "denormals" (those very small numbers that can no longer use the implied leading "1" and have a special FP representation). Why would you want to do this? Because some machines (in particular, some older Pentium 4s) get really, really slow when processing denormals. Others just get somewhat slower. If your application doesn't really need these very small numbers, flushing them to zero is a good solution. Good places to consider this are the last steps of any IIR filters or decay functions.

请参见:为什么将0.1f更改为0会使性能降低10倍?

和http://en.wikipedia.org/wiki/Denormal_number

假设我们正在使用适合16位寄存器的玩具浮点数。有一个符号位,一个5位指数和一个10位尾数。

这个浮点数的值是尾数,解释为二进制十进制值,乘以2的指数次方。

在1附近,指数等于0。尾数中最小的数字是1024的1分之一。

接近1/2的指数是- 1,所以尾数最小的部分是一半大。如果是5位指数,它可以达到负16,此时尾数最小的部分值为3200万分之一。在- 16指数处,这个值大约是32k的1分之1,比我们上面计算的1附近更接近于0 !

这是一个玩具式的浮点模型,它不能反映真正的浮点系统的所有怪癖,但是它反映小于的值的能力与真正的浮点值相当相似。

假设系统无法区分1.000000000000000000000和1.00000000000000001。这是1.0和1.0 + 1e-20。你认为在-1e-20和+1e-20之间还有一些值可以表示吗?