在这个例子中,在FP上使用==是否安全

时间:2014-01-21 01:53:26

标签: c++ floating-point

我偶然发现了这段代码here

Generators doubleSquares(int value)
{
    Generators result;
    for (int i = 0; i <= std::sqrt(value / 2); i++) // 1
    {
        double j = std::sqrt(value - std::pow(i, 2)); // 2
        if (std::floor(j) == j) // 3
            result.push_back( { i, static_cast<int>(j) } ); // 4
    }
    return result;
}

我认为// 3是危险的我错了吗?

3 个答案:

答案 0 :(得分:10)

C ++标准无法保证此代码能够按预期工作。

即使输入具有整数值且数学结果可以精确表示,某些低质量数学库也不会为pow返回正确的舍入值。 sqrt也可能返回一个不准确的值,尽管这个函数更容易实现,因此不太容易出现缺陷。

因此,无法保证j正好是您所期望的整数。

在高质量的数学库中,powsqrt将始终在数学结果完全可表示时返回正确的结果(零错误)。如果你有一个高质量的C ++实现,这个代码应该按照需要工作,直到使用的整数和浮点类型的限制。


改进守则

此代码没有理由使用pow; std::pow(i, 2)应为i*i。这导致精确算术(直到整数溢出点)并完全避免pow是否正确的问题。

取消pow只留下sqrt。如果我们知道实现返回正确的值,我们可以接受使用sqrt。如果没有,我们可以改用:

for (int i = 0; i*i <= value/2; ++i)
{
    int j = std::round(std::sqrt(value - i*i));
    if (j*j + i*i == value)
        result.push_back( { i, j } );
}

此代码仅依赖sqrt在.5内返回准确的结果,即使是低质量的sqrt实现也应提供合理的输入值。

答案 1 :(得分:1)

有两个不同但相关的问题:

  1. j是整数吗?
  2. j可能是双重计算的结果,其精确结果是整数吗?
  3. 引用的代码询问第一个问题。问第二个问题是不正确的。需要更多背景来确定应该问哪个问题。

    如果要问第二个问题,则不能仅依赖floor。考虑一个大于2.99999999999但小于3的双精度。它可能是计算的结果,其精确值为3.它的底数是2,它比它的最大值大1。你需要比较接近std:round的结果。

答案 2 :(得分:0)

我会说这很危险。人们应该总是通过比较两个数字之间的差异和可接受的小数来测试浮点数的“相等性”,例如:

#include <math.h>
...
if (fabs(std::floor(j) - j) < eps) {
...

...其中eps是一个对于一个人来说可接受的小数字。除非可以保证操作返回精确结果,否则这种方法是必不可少的,这在某些情况下可能是正确的(例如符合IEEE-754标准的系统),但C ++标准并不要求这样做。例如,请参阅Cross-Platform Issues With Floating-Point Arithmetics in C++