C++ 是';常数';双重复制+;比较安全?
我注意到有很多关于浮点计算错误的讨论,这要求您使用比C++ 是';常数';双重复制+;比较安全?,c++,floating-point,C++,Floating Point,我注意到有很多关于浮点计算错误的讨论,这要求您使用比==更复杂的比较。然而,所有这些文章似乎都假设该值被操纵(或双重计算),而我没有看到一个包含非常简单的常量复制的示例 请考虑以下事项: const double magical_value = -10; class Test { double _val; public: Test() : _val(magical_value) { } bool is_special() {
==
更复杂的比较。然而,所有这些文章似乎都假设该值被操纵(或双重计算),而我没有看到一个包含非常简单的常量复制的示例
请考虑以下事项:
const double magical_value = -10;
class Test
{
double _val;
public:
Test()
: _val(magical_value)
{
}
bool is_special()
{
return _val == magical_value;
}
};
据我所知,magical_value
应该在编译时设置,以便所有舍入都发生在这一点上。之后,只需将值复制到类中,并与原始值进行比较。这样的比较能保证安全吗?或者复制或比较是否会在此处引入错误
请不要建议其他比较或神奇价值使用方法,这是另一个话题。我只是好奇这个假设
编辑:只是要注意,我有点担心在某些体系结构上,优化可能会导致将值复制到不同大小的浮点寄存器,从而导致精确值的差异。有这样的风险吗
这样的比较能保证安全吗?或者复制或比较是否会在此处引入错误
是,安全(这是复制操作的一项要求,如=
所示)。只要源和目标类型相同,就不需要担心转换/促销
但是,请注意,magical_值
可能不完全包含10
,而是近似值。此近似值将被复制到\u val
给定
const
限定符,有可能magical_value
会被优化掉(如果您启用优化)或按原样使用(即,可能不会耗尽内存)。除了可能大小不同的寄存器外,您还需要担心非规范化浮点(cq刷新为零)(请参阅)
为了让大家了解这可能导致的怪异,请尝试以下代码:
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);
这将给出:(编译时没有刷新到零)
或者:(使用gcc-ffast数学编译)
最后一行当然是奇数:b==bb&&b=0.0f&&bb==0.0f
为真
因此,如果您仍在考虑比较浮点值,至少不要使用小值
更新为了抵消由于使用浮点数而不是双精度而引起的一些评论,它也适用于双精度,但您需要将常数设置为低于DBL_MIN
,例如1e-309
更新2与下面的一些评论相关的代码示例。这表明double也存在问题,并且比较可能变得不一致(当启用flush to zero时)
输出:
b==c 0
assigned a=b: a==b 1
assigned a=c: a==b 1
问题显示在最后一行中,在使用c分配a=c
后,您会天真地期望a==b
将变为false=b
运行时是否会更改\u val
的值?是的,它可以更改为实际值(但肯定是-1
)。我不确定是否有必要将其“重置”为特殊值。我更希望复制浮点值不会改变它,并且给定两个相同原语类型的变量a
和b
,赋值a=b
会导致a==b
为真。然而,我是基于常识来做这个假设的,这在过去让我很失望。@Rook,前提是浮点不是任何计算的结果。从引入计算的那一刻起,您就开始涉及诸如扩展精度之类的内容。再加上一个优化器,您永远无法真正知道代码中实际发生了哪些赋值。因此,将任何东西与可能已计算的任何东西进行比较可能会导致精度问题。(当然,低值严格整数总是精确的)这与我的陈述相切。假设float a=2f/7f代码>,如果浮点b=a,则仍应如此
那么b==a
应该为真,对吗?这正是我的评论的目的。我高度怀疑是否有一种实现,10
或-10
不能准确地表示为双精度
。在某些体系结构上,是否存在将值复制到不同精度寄存器的风险,从而引入潜在差异?@Henrik:不是大约值为10。此外,许多人倾向于忽略浮点类型使用尾数指数表示的事实。@MichałGórny:magical_value
是一个const
,可能永远看不到曙光(不断传播)。剩下要检查的是\u val
的情况。[1] 鉴于\u val
可能持有也可能不持有magical\u value
的值,您不能依赖=
进行相等性测试。[2] 请注意,您使用的是本机浮点类型,即double
(其实际大小可能因架构而异)double
(和int
)是升级为类型的,因为它们是寄存器标准中最好的抽象。这应该可以解决任何与寄存器相关的问题。@MicthałGórny:我的意思是:=
在副本上工作正常。复制的语义保证了这一点。但是,如果\u val
可以有另一个值(比如通过某个表达式赋值),那么当然不能仅依靠==
,而需要考虑浮点差异。-1。请参阅我在回答中的评论。我已经指出了这段代码的足够多的问题。“我真的看不出你想证明什么,”肮脏的家伙,放松点。警察局
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
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