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
Samuel DEVULDER
Le 12/09/2017 à 11:26, Lucas Levrel a écrit :
J'en perds mon latin, si vous pouviez m'aider...

Je me demande si ca n'est pas lié aux architectures des processeurs
actuels, en particulier à leur gestion du pipeline et des optimisations
qui y sont faites.
Tout le monde sait qu'en interne il travaille en format étendu et les
sérialise en mémoire au fortamt 64 bis IEEE754 avec une perte de
précision. Dans ton code, avec un cpu moderne au gros pipeline, il va se
retrouver dans le pipe avec les instructions:
<sauver registre fp-i à l'adresse x>
<... rien qui touche à fp-i ou à l'adresse x...>
<... rien qui touche à fp-i ou à l'adresse x...>
<charger fp-j avec l'adresse x>
Et c'est sur cette dernière étape qu'il détecte une optimisation
réalisable: au lieu de charger depuis la mémoire, il sait que le
registre fp-i contient toujours la bonne valeur et replace cette
instruction de chargement depuis la mémoire en une instruction de
transfert de registre à registre (de fp-i vers fp-j), ce qui est bien
plus rapide. Plus rapide, certes, mais aussi plus précise car là on
transfert le format étendu sans avoir à passer par la valeur tronquée
lors de l'écriture mémoire. Donc la valeur n'est pas exactement celle
attendue après un passage en mémoire, et hop ton prog détecte un problème.
En revanche avec le débugger et le pas à pas, tu pollue le pipeline avec
des instructions qui toutes les chances que ne pas respecter le pattern
attendu par l'optimisateur, et du coup l'instruction "charger fp-j"
s’exécute vraiment, et tu récupère une valeur tronquée comme attendue.
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.
a+
sam.
Avatar
Alain Ketterlin
Lucas Levrel writes:
=============
(...)
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 9),
il s'arrête avec ce message :
Les bins 8.992970000000e-01 (gr1) et 8.992969000000e-01 (gr2) ne correspo ndent pas.
Il a donc lu dans les deux variables des valeurs différentes à partir
de caractères identiques dans les fichiers de données !

Voir le message de Samuel pour une raison possible : le seul fait
d'écrire un double en mémoire peut changer sa valeur.
Il y a des options pour contrôler cela. Par exemple avec gcc :
| '-fexcess-precision=STYLE'
| This option allows further control over excess precision on
| machines where floating-point registers have more precision than
| the IEEE 'float' and 'double' types and the processor does not
| support operations rounding to those types. By default,
| '-fexcess-precisionst' is in effect; this means that operations
| are carried out in the precision of the registers and that it is
| unpredictable when rounding to the types specified in the source
| code takes place. When compiling C, if
| '-fexcess-precision=standard' is specified then excess precision
| follows the rules specified in ISO C99; in particular, both casts
| and assignments cause values to be rounded to their semantic types
| (whereas '-ffloat-store' only affects assignments). This option is
| enabled by default for C if a strict conformance option such as
| '-std9' is used.
|
| '-fexcess-precision=standard' is not implemented for languages
| other than C, and has no effect if '-funsafe-math-optimizations' or
| '-ffast-math' is specified. On the x86, it also has no effect if
| '-mfpmath=sse' or '-mfpmath=sse+387' is specified; in the former
| case, IEEE semantics apply without excess precision, and in the
| latter, rounding is unpredictable.
En particulier, si c'est bien le problème, il devrait disparaître avec
-mfpmath=sse (vérifie quand même que le code résultant n'u tilise plus
x87, je crois me souvenir qu'il y a des fois des subtilités).
-- Alain.
Avatar
espie
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...
On peut jouer avec les options de compile, mais ca fait du code totalement
non portable...
Avatar
pehache
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.

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))
Avatar
Alain Ketterlin
(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 f aut
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.
Je comprends son problème : il doit recoller des données, il n'y a pas
de calcul intermédiaire, il veut vérifier que ses données co rrespondent
immédiatement après lecture.
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).
-- Alain.
Avatar
Samuel DEVULDER
Le 12/09/2017 à 15: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))

Oui... tout dépend du type déclaré pour les différentes variables.
Avatar
Samuel DEVULDER
Le 12/09/2017 à 17:05, Samuel DEVULDER a écrit :
Le 12/09/2017 à 15: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))

Oui... tout dépend du type déclaré pour les différentes variables.

Et aussi tout dépend de l'implementation utilisée pour le printf() des
valeur flottantes en cas d'erreur.
Avatar
espie
In article ,
Alain Ketterlin wrote:
(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.
Je comprends son problème : il doit recoller des données, il n'y a pas
de calcul intermédiaire, il veut vérifier que ses données correspondent
immédiatement après lecture.
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).

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.
Avatar
Lucas Levrel
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).
--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
Samuel DEVULDER
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.
1 2