[英]Comparing floating point values converted from strings with literals
這並不是着名的浮動點數學的重復,即使它看起來像乍一看。
我正在使用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的轉換是否會導致唯一的二進制表示形式,或者可能存在輕微的實現依賴性差異?
從c ++標准:
[lex.fcon]
...如果縮放值在其類型的可表示值范圍內,則結果是可表示的縮放值,否則最接近縮放值的較大或較小可表示值,以實現定義的方式選擇 ...
強調我的。
因此,如果值可以通過double嚴格表示,則只能依賴於相等性。
關於C ++, 從cppreference可以讀到 :
[lex.fcon]
(§6.4.4.2)
評估浮點常量的結果是最接近的可表示值或緊鄰最近的可表示值的較大或較小的可表示值,以實現定義的方式選擇(換句話說,轉換期間的默認舍入方向是實現定義的) 。
由於未指定浮動文字的表示,我猜你無法總結它與scanf
結果的比較。
關於C11(標准ISO / IEC 9899:2011):
[lex.fcon]
(§6.4.4.2)
推薦做法
7浮點常量的轉換時間轉換應該與庫函數(如
strtod
字符串執行時轉換相匹配,給定適合兩種轉換的匹配輸入,相同的結果格式和默認的執行時間舍入。
很明顯,對於C11來說,這並不能保證匹配。
如果字符串與文字相同,那么使用
==
的比較在所有情況下都是真的嗎?
尚未探討的共同考慮因素: FLT_EVAL_METHOD
#include <float.h>
...
printf("%d\n", FLT_EVAL_METHOD);
2評估
long double
類型的范圍和精度的所有操作和常量。
如果返回2,那么value == 7.7
使用的數學是long double
, 7.7
是7.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 <stdio.h>
#include <float.h>
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 <assert.h>
unsigned long long double_order(double x) {
union {
double d;
unsigned long long ull;
} u;
assert(sizeof(double) == sizeof(unsigned long long));
u.d = 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替換它,一切都很好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.