比较从字符串转换的浮点值与文字

这并不是着名的浮动数学数据的重复,即使它看起来像乍一看。

我正在使用fscanf(file, "%lf", &value);从文本文件中读取一个double fscanf(file, "%lf", &value); 并将它与==运算符与双字面值进行比较。 如果字符串与文字相同,那么在所有情况下使用==的比较是否都是true

文字文件内容:

 7.7 

代码段:

 double value; fscanf(file, "%lf", &value); // reading "7.7" from file into value if (value == 7.7) printf("strictly equal\n"); 

预期和实际产出是

 strictly equal 

但是这假设编译器将双文字7.7转换为与fscanf函数完全相同的双fscanf ,但编译器可能会也可能不会使用相同的库将字符串转换为double。

或者另有要求:从字符串到double的转换是否会产生唯一的二进制表示forms,或者可能存在轻微的实现依赖性差异?

现场演示

关于C ++, 从cppreference可以读到 :

[lex.fcon] (§6.4.4.2)

评估浮点常量的结果是最接近的可表示值或紧邻最近的可表示值的较大或较小的可表示值,以实现定义的方式选择(换句话说,转换期间的默认舍入方向是实现定义的) 。

由于未指定浮动文字的表示,我猜你无法总结它与scanf结果的比较。


关于C11(标准ISO / IEC 9899:2011):

[lex.fcon] (§6.4.4.2)

推荐做法

7浮点常量的转换时间转换应与库函数(如strtod字符串执行时转换相匹配,给定适合两种转换的匹配输入,相同的结果格式和默认的执行时间舍入。

很明显,对于C11来说,这并不能保证匹配。

从c ++标准:

[lex.fcon]

…如果缩放值在其类型的可表示值范围内,则结果是可表示的缩放值,否则最接近缩放值的较大或较小可表示值,以实现定义的方式选择

强调我的。

因此,如果值可以通过double严格表示,则只能依赖于相等性。

如果字符串与文字相同,那么在所有情况下使用==的比较是否都是真的?

尚未探讨的共同考虑因素: FLT_EVAL_METHOD

 #include  ... printf("%d\n", FLT_EVAL_METHOD); 

2评估long double类型的范围和精度的所有操作和常量。

如果返回2,那么value == 7.7使用的数学是long double7.77.7L 。 在OP的情况下,这可能会评估为假。

为了考虑这种更宽的精度,请指定将删除所有额外范围和精度的值。

 scanf(file, "%lf", &value); double seven_seven = 7.7; if (value == seven_seven) printf("strictly equal\n"); 

IMO,这是一个更可能发生的问题,而不是变量舍入模式或库/编译器转换的变化。


请注意,这种情况类似于下面的一个众所周知的问题。

 float value; fscanf(file, "%f", &value); if (value == 7.7) printf("strictly equal\n"); 

示范

 #include  #include  int main() { printf("%d\n", FLT_EVAL_METHOD); double value; sscanf("7.7", "%lf", &value); double seven_seven = 7.7; if (value == seven_seven) { printf("value == seven_seven\n"); } else { printf("value != seven_seven\n"); } if (value == 7.7) { printf("value == 7.7\n"); } else { printf("value != 7.7\n"); } return 0; } 

产量

 2 value == seven_seven value != 7.7 

替代比较

为了比较两个彼此“接近”的double ,我们需要一个“近”的定义。 一种有用的方法是将所有有限double值分类为升序,然后将它们的序列号相互比较。 double_distance(x, nextafter(x, 2*x) – > 1

以下代码对double布局和大小做出了各种假设。

 #include  unsigned long long double_order(double x) { union { double d; unsigned long long ull; } u; assert(sizeof(double) == sizeof(unsigned long long)); ud = x; if (u.ull & 0x8000000000000000) { u.ull ^= 0x8000000000000000; return 0x8000000000000000 - u.ull; } return u.ull + 0x8000000000000000; } unsigned long long double_distance(double x, double y) { unsigned long long ullx = double_order(x); unsigned long long ully = double_order(y); if (x > y) return ullx - ully; return ully - ullx; } .... printf("%llu\n", double_distance(value, 7.7)); // 0 printf("%llu\n", double_distance(value, nextafter(value,value*2))); // 1 printf("%llu\n", double_distance(value, nextafter(value,value/2))); // 1 

或者只是使用

 if (nextafter(7.7, -INF) <= value && value <= nextafter(7.7, +INF)) { puts("Close enough"); } 

没有保证。

您可以希望编译器使用高质量的算法来转换文字,并且标准库实现也使用高质量的转换,并且两个高质量的算法应该经常达成一致。

它们也可能使用完全相同的算法(例如,编译器通过将字符放入char数组并调用sscanf来转换文字。

BTW。 我有一个错误,因为编译器没有完全转换文字999999999.5。 用9999999995 / 10.0替换它,一切都很好。