Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Probl

Aucune réponse
Avatar
Lucas Levrel
Bonjour,

Je rencontre un problème très étrange dans un programme de traitement de
données que j'ai écrit. Il lit ligne par ligne puis colonne par colonne
deux fichiers en parallèle, qui doivent avoir la même première colonne.

En l'occurrence, les deux fichiers commencent par les données suivantes
(les lignes commençant par # sont sautées par le programme) :
==> test1 <==
# abscisse recalculée avec pas régulier de 0,029976565 puis arrondie à
10⁻⁷
# r(bohrs) gCuCu gZrZr gCuZr
0.0000000 0.00000E+00 0.00000E+00 0.00000E+00
0.0299766 0.00000E+00 0.00000E+00 0.00000E+00
0.0599531 0.00000E+00 0.00000E+00 0.00000E+00
0.0899297 0.00000E+00 0.00000E+00 0.00000E+00
==> test2 <==
# r gCuCu gZrZr gCuZr
0.0000000 0.0000000 0.0000000 0.0000000
0.0299766 0.0000000 0.0000000 0.0000000
0.0599531 0.0000000 0.0000000 0.0000000
0.0899297 0.0000000 0.0000000 0.0000000
=============

Le bout de code problématique :
=============
(...)
while(lecture){
if(fscanf(gr1,"%lf",&(colonnes_a[0])) !=1) break;
if(fscanf(gr2,"%lf",&(colonnes_b[0])) !=1) break;
if(colonnes_a[0] != colonnes_b[0]){
error(0,0,"Les bins %.12e (gr1) et %.12e (gr2)"
" ne correspondent pas.",colonnes_a[0],colonnes_b[0]);
quit();
}
for(int i=1; i<Ncolonnes; i++){
(...)
=============

Lorsque j'exécute le programme (compilé avec gcc 4.5.0 et -std=c99), il
s'arrête avec ce message :
Les bins 8.992970000000e-01 (gr1) et 8.992969000000e-01 (gr2) ne correspondent pas.

Il a donc lu dans les deux variables des valeurs différentes à partir de
caractères identiques dans les fichiers de données !

Je passe donc au débogueur (ddd) et c'est de plus en plus bizarre :
- si j'exécute ligne par ligne, les valeurs lues dans les deux variables
sont identiques : 0.89929700000000001 et le programme ne s'arrête pas ;
- si je mets un breakpoint sur error() et que j'exécute en continu
jusque-là, cette ligne est atteinte et les variables valent
respectivement 0.89929700000000001 et 0.89929689999999995

J'en perds mon latin, si vous pouviez m'aider...


--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)

10 réponses

1 2
Avatar
Lucas Levrel
Bonjour à tous,
Merci à tous pour vos réponses. Ça fait plaisir de voir que le groupe est
vivant :-)
Je fais un message global pour les diverses réponses.
Le 12/09/2017 à 12:22, Samuel DEVULDER a écrit :
Ceci expliquerait bien ce que tu observes (bug pas en mode pas à pas, et
bug si le code s’exécute pleinne bourre sur le cpu). Pour l'éviter en C,
je pense qu'il pourrait être judicieux de déclarer "volatile" tes
variables doubles incriminées. Une autre possibilité, plus sure, serait
de remplacer le teste de valeur "==" toujours problématique sur les
doubles par un test de précision à 1E-15 près histoire d'être au plus
près du bit de poids faible de la représentation 64bits IEEE754.


Le 12 septembre 2017, à 13:52, pehache a écrit :
ici j'ai l'impression qu'il lit et stocke des valeurs dans des float 32
bits, d'où la différence observée de l'ordre de 10^-7. Le test de
précision devrait donc se faire par rapport à la précision relative d'un
float 32 bits (~6E-7, 1E-6 en pratique))

Merci Samuel pour les explications détaillées. Ceci dit, comme le fait
remarquer pehache, l'écart est ici de 1e-7, or mes variables sont des
double ! (Pas explicite dans le bout de code, mais indicateur de
conversion %lf dans le fscanf.) On est donc loin d'une erreur d'arrondi.
Le 12 septembre 2017, à 14:19, Alain Ketterlin a écrit :
Voir le message de Samuel pour une raison possible : le seul fait
d'écrire un double en mémoire peut changer sa valeur.

Pas à 1e-7 près j'espère :-)
Il y a des options pour contrôler cela. Par exemple avec gcc :

(...)
Merci pour ces infos, que je garde précieusement sous le coude !
(Marc Espie) writes:
Ca peut valoir le coup d'apprendre a bosser avec des flottants, en vrai.


Tiens, j'y avais pas pensé...
En particulier, l'egalite est a peu pres garantie ne jamais marcher, il faut
toujours passer par du fabs(a-b) < epsilon...


Oui mais 1e-7 d'écart avec des double, ce n'est pas admissible. Par
ailleurs, pas d'epsilon facile car un changement d'unité peut tout
multiplier par e-10 par exemple (des fois que l'utilisateur écrive en
mètres plutôt qu'en angströms).
In article ,
Alain Ketterlin wrote:
Dans l'article classique de Goldberg
(http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) :
| Incidentally, some people think that the solution to such anomalies is
| never to compare floating-point numbers for equality, but instead to
| consider them equal if they are within some error bound E. This is
| hardly a cure-all because it raises as many questions as it answers.
| What should the value of E be?


:-)
Je comprends son problème


Ça fait plaisir !
Le 12 septembre 2017, à 17:46, Marc Espie a écrit :
Bah, apres, vu que c'est du fichier texte, s'il veut etre sur que c'est
la meme valeur, autant comparer les chaines, hein.

Oui mais non, il peut y avoir des 0 de remplissage à gauche, voire à
droite, selon le format utilisé par le producteur des données.
Le 12 septembre 2017, à 20:39, Lacide Manu a écrit :
Si vous voulez absolument comparer des flottants, essayer éventuellement long
double et voyez si cela vous convient.

J'utilise déjà des double et l'écart est trop grand pour être expliqué par
des erreurs d'arrondi.
Et consultez également, à ce propos :
https://www.levenez.com/lang/c/faq/fclc011.html#q_4
et https://www.levenez.com/lang/c/faq/fclc011.html#q_5

Je connais tout ça, mais il n'est pas question ici de calcul avec des
flottants, simplement de lecture.
--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
Lucas Levrel
Le 13 septembre 2017, à 10:36, Samuel DEVULDER a écrit :
Le 13/09/2017 à 10:25, Lucas Levrel a écrit :
j'avais complètement zappé le facteur 10

Où ca le facteur 10. Pour autant que je vois bien, il n'y a pas un tel écart
dans les valeurs numériques que tu nous as fourni.

Les valeurs fautives sont 8.99e*-1* or la dernière ligne citée des
fichiers test est à 8.99e*-2*.
pehache a été très perspicace sur ce coup-là, mes fichiers test sont
évidemment beaucoup plus longs, et la première colonne est une suite
arithmétique, on trouve donc bien des valeurs 8.99e-1 mais vers la 40e
ligne de données !
--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
pehache
Le 12/09/2017 à 16:28, Alain Ketterlin a écrit :
(Marc Espie) writes:
Ca peut valoir le coup d'apprendre a bosser avec des flottants, en vrai.
En particulier, l'egalite est a peu pres garantie ne jamais marcher, il
faut
toujours passer par du fabs(a-b) < epsilon...

Dans l'article classique de Goldberg
(http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) :
| Incidentally, some people think that the solution to such anomalies is
| never to compare floating-point numbers for equality, but instead to
| consider them equal if they are within some error bound E. This is
| hardly a cure-all because it raises as many questions as it answers.
| What should the value of E be? If x < 0 and y > 0 are within E, should
| they really be considered to be equal, even though they have different
| signs? Furthermore, the relation defined by this rule, a ~ b <=> |a -
| b| < E, is not an equivalence relation because a ~ b and b ~ c does
| not imply that a ~ c.

En toute rigueur il a raison, mais ça veut juste dire que ce n'est pas une
règle générale et qu'il faut l'adapter à chaque cas. Parfois le epsilon
qui va bien sera celui de la précision machine, parfois de la précision
des calculs, parfois un epsilon relatif, parfois un epsilon absolu... Bref,
il faut réfléchir :-) !
On peut jouer avec les options de compile, mais ca fait du code totalement
non portable...

Il n'y a pas grand chose de portable sur les flottants, sinon IEEE 754
du sol au plafond, qui est ce que signifiait l'option que je proposais
(au moins pour le diagnostic).

De manière générale il est quand même préférable de traiter les
problèmes de ce genre par du code plutôt que de dépendre d'options de
compilation.
Avatar
pehache
Le 13/09/2017 à 10:25, Lucas Levrel a écrit :
Bonjour,
Le 12 septembre 2017, à 13:48, pehache a écrit :
==> test1 <= >>> 0.0899297 0.00000E+00 0.00000E+00 0.00000E+00
==> test2 <= >>> 0.0899297 0.0000000 0.0000000 0.0000000
============ >
Les bins 8.992970000000e-01 (gr1) et 8.992969000000e-01 (gr2) ne
correspondent pas.

C'est donc vers la 40ème ligne du fichier, non ? As-tu bien vérifié
dans
le fichier que les valeurs sont réellement identiques ?

Y'en a un qui suit ! Merci de m'avoir dessillé, j'avais complètement
zappé
le facteur 10 ! Évidemment en exécutant ligne par ligne je m'arrêtais à
la
ligne 4 qui est dix fois plus petite... Et l'écart détecté existe bel et
bien vers la ligne 40.
Maintenant je vais chercher d'où vient cet écart, mais c'est une autre
histoire (pas en C malheureusement).

Donc du coup les valeurs sont bel et bien différentes dans les deux
fichiers ?
Avatar
pehache
Le 13/09/2017 à 10:43, Lucas Levrel a écrit :
En particulier, l'egalite est a peu pres garantie ne jamais marcher, il
faut
toujours passer par du fabs(a-b) < epsilon...


Oui mais 1e-7 d'écart avec des double, ce n'est pas admissible. Par
ailleurs, pas d'epsilon facile car un changement d'unité peut tout
multiplier par e-10 par exemple (des fois que l'utilisateur écrive en
mètres plutôt qu'en angströms).

Pour ça il faut faire un test en relatif plutôt qu'en absolu :
|a-b| <= max(|a|,|b|) * epsilon
Avatar
Lucas Levrel
Le 13 septembre 2017, à 09:53, pehache a écrit :
Donc du coup les valeurs sont bel et bien différentes dans les deux
fichiers ?

Oui.
--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
Samuel DEVULDER
Le 13/09/2017 à 15:01, pehache a écrit :
Ben la valeur fautive est 10 fois plus élevée que la dernière valeur
affichée

Oula... oh oui je vois. Balèze!
Avatar
espie
In article ,
Lucas Levrel wrote:
Le 12 septembre 2017, à 17:46, Marc Espie a écrit :
Bah, apres, vu que c'est du fichier texte, s'il veut etre sur que c'est
la meme valeur, autant comparer les chaines, hein.

Oui mais non, il peut y avoir des 0 de remplissage à gauche, voire à
droite, selon le format utilisé par le producteur des données.

T'es un grand garcon. Virer des zeros ca se fait.
Si vraiment l'idee c'est de s'assurer que tu as exactement la meme valeur,
independamment du logiciel de production, la seule facon portable de faire
ca va etre de bosser sur les representations texte.
Ou alors de regarder ce que tu peux faire comme representation intermediaire
mantisse + exposant qui te permette a coup sur d'avoir les memes valeurs.
Avatar
Samuel DEVULDER
Le 13/09/2017 à 16:13, Marc Espie a écrit :
ca va etre de bosser sur les representations texte.
Ou alors de regarder ce que tu peux faire comme representation intermediaire
mantisse + exposant qui te permette a coup sur d'avoir les memes valeurs.

Le printf("%a") est peut-être un truc intéressant à utiliser pour
stocker en ASCII sans faire intervenir d'erreur d'arrondis lors de la
conversion en base 10 de la partie décimale. En effet le %a affiche un
float en hexa sans aucune conversion de base.
printf("pi=%an", 3.14); ---> pi=0x1.91eb86p+1
^^^^^^
tous les bits IEEE754 sont là
a+
sam.
Avatar
Lucas Levrel
Le 13 septembre 2017, à 17:15, Samuel DEVULDER a écrit :
Le printf("%a") est peut-être un truc intéressant à utiliser pour stocker en
ASCII sans faire intervenir d'erreur d'arrondis lors de la conversion en base
10 de la partie décimale. En effet le %a affiche un float en hexa sans aucune
conversion de base.
printf("pi=%an", 3.14); ---> pi=0x1.91eb86p+1
^^^^^^
tous les bits IEEE754 sont là

J'ai découvert la conversion %a hier en cherchant à comprendre pourquoi
les deux programmes différents donnaient des arrondis différents.
Je n'ai pas trouvé de logiciel calculatrice (CLI ou GUI) qui puisse
afficher/convertir les flottants entre les différentes bases. Est-ce que
ça existe ?
--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
1 2