浮点值与 0.0 的比较

Comparison to 0.0 with floating point values

提问人:Johnathan Gross 提问时间:10/12/2017 最后编辑:Johnathan Gross 更新时间:10/12/2017 访问量:4240

问:

使用浮点数时,有时会出现舍入问题。因此,通常不建议将计算结果与 == 或 != 进行比较,而是使用适当的边界,如 。abs(a-b)<1.0e-10

但是用它来比较是否合适?0.0

这就是我在说的:

double foo(){
    //code which can return values that aren't 0.0
    //all returns are either literals or global variables defined with literals
    return 0.0;
}

这里将始终返回 exactly 或明确不是 的数字。具体而言,这些数字的量级均大于但小于 。f0.00.00.12.0

如果它返回,将永远如此?是否有可能 if 返回的值不是 ?0.0==foo()true0.00.0==foo()truefoo0.0

C++ 浮点 双重 相等

评论

5赞 Cory Kramer 10/12/2017
“如果 0.0==foo() 返回 0.0,它会一直为 true 吗?”是的。
2赞 10/12/2017
但是,当然,您必须确保它明确地返回 0.0,而让它这样做的唯一真正可靠的方法是返回 0.0 文本,而不是计算结果。
2赞 Thomas Matthews 10/12/2017
必读:每个计算机科学家都应该知道的浮点运算知识
2赞 tadman 10/12/2017
当心和其他野兽。-0.0
1赞 Slava 10/12/2017
我会使用 or .这将使意图明确并消除疑虑。std::optionalboost::optional

答:

1赞 lorro 10/12/2017 #1

以你为安全。这是因为尾数是整数类型(通常是特定于平台的 uint;然后通过符号位和指数将其扩展为浮点数)。std::numeric_limits::digits<float>

http://en.cppreference.com/w/cpp/types/numeric_limits/digits

0赞 Clifford 10/12/2017 #2

如果你返回一个文字,那么是的,但这通常不是一个好主意,因为虽然今天是这样,但你能确定在维护或代码重用下它会保持正确吗?最好编写一些可以在更广泛的环境中工作的东西。在这种情况下:0.0

foo() < 0.1

对于您指定的所有值,将返回 false。更通用的解决方案是测试是否足够接近零:

static const EPSILON = 0.00001 ;
std::fabs( foo() - 0.0 ) < EPSILON ; 

最好使用上述模式之一,因为它不需要对“零”的精度做出任何保证。foo()

评论

0赞 lorro 10/12/2017
请注意,OP 的编写完全相同(即,想要避免第二个)。
0赞 Clifford 10/12/2017
@lorro:我不确定他想避免它;他只是在问他的替代方案是否合适。我的观点是,比较文字 0.0 在有限的一组情况下有效,这些情况可能不会随着时间的推移而成立(代码更改并被重用,或者同事在不安全的情况下复制您的代码),因此最好避免。
0赞 lorro 10/12/2017
除了 -0.0、% 和 / 之外,它基本上与给定尾数的 am int 一样工作,因此在某些情况下它是安全有效的。实际上,如果我没记错的话,一些大型机使用 float 作为指数为 1 的 int 的基础类型。
0赞 lorro 10/12/2017
第一段,最后一句话和下一句话让我觉得是这样。(是的,从技术上讲,答案是正确的 - 我只是说在OP的用例中 - 并且广义的用例 - 它是安全的)。
0赞 Clifford 10/12/2017
我并不是说在给出的例子中它不安全。如果以后被修改,你必须记住你做了这样的假设,并维护它们或破坏代码。计算值可能具有精度误差,使其接近但不精确为零。foo()
1赞 aka.nice 10/12/2017 #3

在您的情况下,使用浮点相等是完全正确的。== 0.0

它完全符合函数的意图(如果失败,则返回某个值或 0.0)。使用任何其他 epsilon 在某种程度上是任意的,并且需要了解正确值的范围。如果有什么变化,那很可能是值的范围而不是 0,因此测试的未来证明并不比其他解决方案 IMO 差。== 0.0

我看到的唯一问题是一些编译器会警告相等(-Wfloat-equal)的可疑使用......这与警告一样有用,因为这样的指令可能会导致问题(整数溢出和未定义的行为)。奇怪的是,我从未看到第二个警告。int a,b,c; ...; c=a+b;

因此,如果你想使用 -Wall -Werror 编译器选项来证明未来,你可以以不同的方式对失败进行编码(例如使用负值)并测试 foo < 0.0 - 直到有人发现浮点不等式可能也需要容差并将构造声明为可疑。