我的意思是:我有一种情况,我在
double
类型的结构中有两个字段,两个字段都可以通过一些评估设置在那里,但在某些情况下,第二个字段可以等于第一个字段,但当这种情况发生时,它只是将第二个字段分配给第一个字段的值。
现在,为了认识到这种情况,我使用
==
运算符来检查是否发生了这种情况。编译器(gcc)抱怨这种比较不安全。但是,如果由于一些小的 epsilon 差异而导致值不完全相等,因此结果为 false,那么这仍然是可以接受的(意思是,如果它们结果不相等,尽管单独评估它们的计算应该提供相同的结果)。通过此比较,我唯一需要检测的是第二个字段被分配了一个直接从第一个字段获取的值。
在这种情况下使用
==
运算符是否安全,或者我真的应该添加一些额外的布尔字段来记录它并仅依赖于此吗?
示例:
double a, b;
a = calculate_A();
b = calculate_B();
if (a == b) // results false, although calculations should ship the same: THAT'S STILL OK.
a = calculate_A();
b = a;
if (a == b) // can I rely on that this results ALWAYS TRUE in this case?
简而言之,64 位
double
类型为其精度分配了约 52 位,相当于约 15/16 位(取决于体系结构)。如果超过这个精度,您就会淹没双精度数的精度位,导致您处于所有赌注都关闭的未定义区域(见下文)。
在此示例中,精度精确到小数点后第 19 位,但这取决于编译器。不要相信这个。任何超出小数点后第 16 位的内容都没有明确定义,因为它不适合内存。
同样的原则适用于重要的转换,例如将字符串转换为双精度型(见下文)。
在 C++ 中,double 符合 IEEE 754 binary64 格式。这意味着当定义两个相同值的双精度数时,例如。
a = b = calcWhatever()
,根据设计,它们将包含完全相同的精度序列。如果没有明确定义,那么在程序的不同部分/文件中定义两个数字将意味着不同的数字,这是不可能或理智的(见下文)。
std::string a_str = "0.0003253";
std::string b_str = "0.0003253";
double a = stod(a_str);
double b = stod(b_str);
std::cout<< std::setprecision(30) << a <<std::endl;
std::cout<< std::setprecision(30) << b <<std::endl;
输出:
0.000325299999999999992373461710216
0.000325299999999999992373461710216
双精度数包含相同的精确值。尽管精度设置为超出可接受的限制,但编译器将遵守 IEEE 格式并定义相同的小数点序列,因为它将看到第一个定义并重用它。
据我所知,两个双精度数不具有完全相同的精度的唯一原因是以下原因:
0.3 == 0.2 + 0.1 /* 挪威克朗 */
0.001 == 1.0/1000 /* 好的 */
2.5 == 2.5 * (5.0/5.0) /* 好的 */
浮动到双倍 /* 不精确 */
字符串转双精度 /* 精确 */
在您的情况下,取决于功能
calculate_A
和 calculate_B
例如。 constexpr,双打可能是明确定义的,但这是一个非常高的风险,不值得冒。第二种情况始终是明确定义的,并且 a == b
应始终返回 true。