Comparaison des valeurs en virgule flottante converties à partir de chaînes avec des littéraux

Ce n’est pas une copie du fameux calcul Is en virgule flottante , même s’il en a l’air à première vue.

Je lis un double d’un fichier texte en utilisant fscanf(file, "%lf", &value); et en le comparant avec l’opérateur == à un double littéral. Si la chaîne est identique au littéral, la comparaison utilisant == sera-t-elle true dans tous les cas?

Exemple

Contenu du fichier texte:

 7.7 

Extrait de code:

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

La sortie attendue et réelle est

 ssortingctly equal 

Mais cela suppose que le compilateur convertisse le double littéral 7.7 en un double exactement de la même manière que la fonction fscanf , mais le compilateur peut ou non utiliser la même bibliothèque pour convertir les chaînes en double.

Ou encore, posez-vous la question suivante: la conversion de chaîne en double donne-t-elle une représentation binary unique ou peut-il y avoir de légères différences liées à la mise en œuvre?

Démonstration en direct

À propos de C ++, de cppreference, on peut lire :

[lex.fcon] (§6.4.4.2)

Le résultat de l’évaluation d’une constante flottante est soit la valeur représentable la plus proche, soit la valeur représentable supérieure ou inférieure immédiatement adjacente à la valeur représentable la plus proche, choisie d’une manière définie par l’implémentation (en d’autres termes, la direction d’arrondi par défaut lors de la traduction est définie par l’implémentation). .

Étant donné que la représentation d’un littéral flottant n’est pas spécifiée, je suppose que vous ne pouvez pas conclure à propos de sa comparaison avec un résultat scanf .


À propos de C11 (norme ISO / IEC 9899: 2011):

[lex.fcon] (§6.4.4.2)

Pratique recommandée

7 La conversion au moment de la traduction des constantes flottantes doit correspondre à la conversion au moment de l’exécution des chaînes de caractères par les fonctions de la bibliothèque, telles que strtod , en fonction des entrées correspondantes adaptées aux deux conversions, du même format de résultat et de l’arrondi par défaut du temps d’exécution.

Donc, clairement pour C11, cela n’est pas garanti.

Du standard c ++:

[lex.fcon]

… Si la valeur mise à l’échelle est comprise dans la plage de valeurs représentables pour son type, le résultat est la valeur mise à l’échelle, si elle est représentable, sinon la valeur représentable la plus grande ou la plus petite se rapprochant de la valeur mise à l’échelle, choisie d’une manière définie par l’implémentation

l’accent mien.

Vous ne pouvez donc compter sur l’égalité que si la valeur est ssortingctement représentable par un double.

Si la chaîne est identique au littéral, la comparaison utilisant == sera-t-elle vraie dans tous les cas?

Une considération commune non encore explorée: FLT_EVAL_METHOD

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

2 évaluer toutes les opérations et constantes à la scope et à la précision du type long double .

Si cela renvoie 2, alors le calcul utilisé dans value == 7.7 est long double et 7.7 traité comme 7.7L . Dans le cas de OP, cela peut être considéré comme faux.

Pour prendre en compte cette précision plus large, atsortingbuez des valeurs qui suppriment toute plage et précision supplémentaire.

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

OMI, il s’agit d’un problème plus probable que d’autres modes d’arrondis ou de conversions de librairies / compilateurs.


Notez que ce cas s’apparente à celui ci-dessous, un problème bien connu.

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

Manifestation

 #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; } 

Sortie

 2 value == seven_seven value != 7.7 

Alternative Comparer

Pour comparer deux double “proches” l’un de l’autre, nous avons besoin d’une définition de “proche”. Une approche utile consiste à considérer toutes les valeurs double finies sortingées dans une séquence ascendante, puis à comparer leurs numéros de séquence les uns des autres. double_distance(x, nextafter(x, 2*x) -> 1

Le code suivant fait diverses hypothèses sur la double disposition et la taille.

 #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 

Ou simplement utiliser

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

Il n’y a pas de garantie.

Vous pouvez espérer que le compilateur utilise un algorithme de haute qualité pour la conversion de littéraux et que l’implémentation de librairies standard utilise également une conversion de haute qualité. Deux algorithmes de haute qualité doivent donc s’accorder assez souvent.

Il est également possible que les deux utilisent exactement le même algorithme (par exemple, le compilateur convertit le littéral en plaçant les caractères dans un tableau de caractères et en appelant sscanf.

BTW. J’ai eu un bogue causé par le fait qu’un compilateur n’a pas converti le 999999999.5 littéral exactement. Il a été remplacé par 9999999995 / 10.0 et tout allait bien.