10

我注意到有很多关于浮点计算错误的讨论,这需要您使用比==. 但是,所有这些文章似乎都假设该值以某种方式被操纵(或双重计算),而我没有看到一个涵盖非常简单的常量复制的示例。

请考虑以下几点:

const double magical_value = -10;

class Test
{
    double _val;

public:
    Test()
        : _val(magical_value)
    {
    }

    bool is_special()
    {
        return _val == magical_value;
    }
};

据我了解,magical_value应该在编译时设置,以便所有舍入都发生在那个时候。之后,该值应该被复制到类中,并与原始值进行比较。这样的比较能保证安全吗?或者复制或比较可以在这里引入错误吗?

请不要建议替代比较或神奇的价值使用方法,那是另一个话题。我只是对这个假设感到好奇。

编辑:请注意,我有点担心在某些体系结构上,优化可能会导致将值复制到不同大小的浮点寄存器,从而引入精确值的差异。有这样的风险吗?

4

2 回答 2

2

这样的比较能保证安全吗?或者复制或比较可以在这里引入错误吗?

是的,安全的(这是复制操作的要求,由 暗示=)。只要源和目标类型相同,您就无需担心任何转化/促销。

但是,请注意,它magical_value可能不包含10精确值,而是一个近似值。这个近似值将被复制到_val.

给定const限定符,magical_value很可能会被优化掉(如果您打开优化)或按原样使用(即可能不会用完内存)。

于 2012-06-13T08:58:50.247 回答
0

除了可能不同大小的寄存器之外,您还需要担心非规范化浮点(cq 刷新为零)(请参阅为什么将 0.1f 更改为 0 会使性能降低 10 倍?

只是为了了解这可能导致的怪异,请尝试以下代码:

float       a = 0.000000000000000000000000000000000000000047683384;
const float b = 0.000000000000000000000000000000000000000047683384;
float aa = a, bb = b;

#define SUPPORT_DENORMALIZATION ({volatile double t=DBL_MIN/2.0;t!=0.0;})

printf("support denormals: %d\n",SUPPORT_DENORMALIZATION);
printf("a = %.48f, aa = %.48f\na==aa %d, a==0.0f %d, aa==0.0f %d\n",a,aa,a==aa,a==0.0f,aa==0.0f);
printf("b = %.48f, bb = %.48f\nb==bb %d, b==0.0f %d, bb==0.0f %d\n",b,bb,b==bb,b==0.0f,bb==0.0f);

这给出了:(编译时没有清零)

support denormals: 1
a = 0.000000000000000000000000000000000000000047683384, aa = 0.000000000000000000000000000000000000000047683384
a==aa 1, a==0.0f 0, aa==0.0f 0
b = 0.000000000000000000000000000000000000000047683384, bb = 0.000000000000000000000000000000000000000047683384
b==bb 1, b==0.0f 0, bb==0.0f 0

或:(用 编译gcc -ffast-math

support denormals: 0
a = 0.000000000000000000000000000000000000000000000000, aa = 0.000000000000000000000000000000000000000000000000
a==aa 1, a==0.0f 1, aa==0.0f 1
b = 0.000000000000000000000000000000000000000047683384, bb = 0.000000000000000000000000000000000000000000000000
b==bb 1, b==0.0f 0, bb==0.0f 1

最后一行当然是奇怪的:b==bb && b!=0.0f && bb==0.0f这是真的。

所以如果你还在考虑比较浮点值,至少要远离小值。

更新以抵消一些关于这是由于使用浮点数而不是双精度数的评论,它也适用于双精度数,但您需要将常量设置为以下某处DBL_MIN,例如1e-309.

更新 2与下面的一些评论相关的代码示例。这表明双精度数也存在问题,并且比较可能会变得不一致(启用刷新为零时)

    double a;
    const double b = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001225;
    const double c = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225;

    printf("b==c %d\n",b==c);
    a = b;
    printf("assigned a=b: a==b %d\n",a==b);
    a = c;
    printf("assigned a=c: a==b %d\n",a==b);

输出:

b==c 0
assigned a=b: a==b 1
assigned a=c: a==b 1

问题显示在最后一行,您会天真地期望a==b在分配a=cwith后会变为错误c!=b

于 2012-06-13T09:52:33.470 回答