'const'双重复制+比较安全吗?

时间:2012-06-13 08:52:18

标签: c++ floating-point

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

请考虑以下事项:

const double magical_value = -10;

class Test
{
    double _val;

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

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

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

请不要建议替代比较或魔法价值使用方法,这是另一个主题。我对这个假设很好奇。

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

2 个答案:

答案 0 :(得分:2)

  

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

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

但是,请注意magical_value可能不包含10,而是近似值。此近似值将被复制到_val

鉴于const限定符,很可能magical_value可能会被优化掉(如果你打开优化)或原样使用(即没有内存可能会用完)。

答案 1 :(得分:0)

除了可能不同大小的寄存器之外,你还要对非规范化浮点(cq flush-to-zero)进行担心(参见Why does changing 0.1f to 0 slow down performance by 10x?

只是想知道这可能导致的奇怪之处,试试这段代码:

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=c之后,您会天真地预期c!=b会变错。