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

Gros probleme étrange de precision pour les long double

25 réponses
Avatar
Cédric BILLOT
Voila, je suis sur un projet qui demande énormément de précision, je travail
donc en long double.
Je calcule la dérivé d'une fonction en un point grace a une methode simple
(f(x+h)-f(x-h))/2h) mais voila,
pour 80% des cas tout va bien; et parfois, je me retrouvais avec 0, alors
que la reponse du calcul fait sous Maple donnais un résultat.
J'ai épuré mon code autour de l'erreur jusqu'a avoir le code suivant.

long double h = 0.00000000000000000000001;
long double a = 10132.2;
if(a == a + h) cout<<"ERREUR DE CALCUL."<<endl;

et la!!!! c'est le drame!!!!

en effet a==a+h pourquoi?
Je ne pense pas pourtant arriver a la limite de précision du long double....

Quelqu'un a-t-il la solution a mon problème???

Merci d'avance,
@+Fab

5 réponses

1 2 3
Avatar
mick.corp
Bonjour,

Etant sur le même projet, j'ai utilisé la même méthode, mais les
différentes méthodes de résolution utilisées avec le programme
(Choleski par exemple), donne des réponses approximatives (numérique
quoi !). Du coup, au lieu de tabler sur la précision à mort, j'ai
plutôt privilégié le côté vitesse de calcul.
Du coup mon h est de l'ordre de 10E-5 et ça marche pas mal je dois
dire (à confirmer par des graphiques)... que ça soit en float ou en
double, il y a convergence (j'utilise pas les long double pour raison
de portabilité).

Autre précision, qui a peut-être son importance : je suis en C et non
en C++ Objet (pour raison de portabilité encore)

@+,
Mick
Avatar
Pierre Maurette
typa:
[...]
La Mantisse est codé sur combien de bit pour le long double?


Pour info, formats des "réels" de la FPU Intel:
Single Double Extended
bits total 32 64 80
bits exposant 8 11 15
bits mantisse 23 52 63
^^


Juste un détail, mais pour l'extended, c'est bien 64.
Si j'avais choisi d'écrire 64, aurions-nous eu droit à "Juste un

détail, c'est 63 plus un bit d'entier etc." ?
Vous connaissez comme moi quel est l'usage (particulier!) du bit
d'entier, efficace dans un domaine (particulier!) de calcul (les
petits nombres).
Il est pertinent et courant (Intel) d'écrire 63 et non 64.


précision (bits) 24 53 64

La mantisse est positive, l'exposant "biasé", c.a.d en gros signé
centré sur 0.


C-à-d en gros, non signé, mais on soustrait une valeur constante pour en
obtenir la valeur réele.
qui de ce fait devient signée. C'est du biased, quoi ...

En fait, la définition est (partiellement):
"on ajoute à la valeur réelle signée une constante telle que le
résultat soit toujours positif".

Si vous faites un contrôle du total des bits, n'oubliez pas le bit du
cygne. Et il n'y a pas d'erreur, l'extended a un format un peu
particulier.


Je dirais plutôt que ce sont les float et les double qui ont un format
un peu particulier:-). C'est eux où on ne représente pas le bit de poids
fort de la mantisse (parce que dans un nombre normalisé, il serait
toujours un).
J'imagine que c'est pour le fun, votre remarque. Ne pas représenter un

bit dont on sait que la valeur est à 1 n'a rien de "particulier",
comme on voit parfois 1/2 écrit ".5" (là, c'est 0 qui est implicite).
En revanche, si vous doutez de la "particularité" de la gestion du bit
d'entier en extended, reportez-vous au data book.

Mais ce que tu documentes, c'est le hardware.
Non? Avais pas remarqué.

En fait, l'OP pose problème de précision. Ça débat autour d'un nombre
de digits significatifs. Je communique "pour info" des données qui
permettent d'accéder (à peu près) par un calcul simple à ce nombre,
pour trois formats normalisés (mais pas au sens du C++), répandus et
issus d'une FPU ayant un certain succès.

Rien n'oblige un
compilateur de l'utiliser, et en fait, avec VC++ 6.0, j'ai
sizeof(double) == sizeof(long double). Les deux vaut 8, c-à-d donc que
le compilateur utilise le type double de la FPU et pour double et pour
long double.
Ça par contre, ça me troue le c.., je n'avais jamais remarqué. C'est

pareil pour VC++7.1 bien entendu. La doc lue en diagonale ne parle
même pas d'un "specific Microsoft (x86)" qui implémenterait un mode
extended en 80 bits. Il est précisé que long double faisait 80 bits en
16-bits x86, mais est équivalent à double (64 bits) en code 32 bits,
en raison d'un choix de compatibilité RISC. Etrange.

Pierre



Avatar
Loïc Joly
Mick Corp wrote:
Autre précision, qui a peut-être son importance : je suis en C et non
en C++ Objet (pour raison de portabilité encore)


Quel est l'environnement ciblé qui possède un compilateur C mais pas C++
? Il y a 15 ans, j'aurais compris cet argument, de nos jours, il me
surprend.

Toujours est-il que ça ne devrait pas avoir son importance pour de la
précision de calcul numérique, si ce n'est que si c'est nécessaire, il
est plus facile de modifier le code pour gérer une plus grande précision
en C++ qu'en C.

--
Loïc

Avatar
kanze
Pierre Maurette <maurette.pierreATfree.fr@> wrote in message
news:...
typa:
[...]
La Mantisse est codé sur combien de bit pour le long double?


Pour info, formats des "réels" de la FPU Intel:
Single Double Extended
bits total 32 64 80
bits exposant 8 11 15
bits mantisse 23 52 63


Juste un détail, mais pour l'extended, c'est bien 64.


Si j'avais choisi d'écrire 64, aurions-nous eu droit à "Juste un
détail, c'est 63 plus un bit d'entier etc." ?


Ce qui veut dire quoi ? Quand tu veux extraire les différents champs,
c'est bien 23 bits, 52 bits et 64 bits qu'il faut chercher dans la
mémoire. Ce qui est intéressant (et je crois que c'est ce que tu essaies
à dire), c'est que la sémantique des bits dans un extended n'est pas
pareil à la sémantique des bits dans les deux autres. Par définition,
pour tout nombre flottant normalisé, le « chiffre » de poids fort de la
mantisse est non-null. Si le flottant est représenté en base 2, comme
c'est le cas ici, alors, le « chiffre » de poids fort est un bit, et
s'il est non-null, il est forcement un. Dans les représentations IEEE,
dont se sert Intel pour les single et les double, on fait l'économie de
ce bit dans la représentation, puisqu'on en connaît la valeur. Dans la
représentation extended d'Intel, en revanche, ce bit est bel et bien
physiquement présent dans la mémoire.

Vous connaissez comme moi quel est l'usage (particulier!) du bit
d'entier, efficace dans un domaine (particulier!) de calcul (les
petits nombres). Il est pertinent et courant (Intel) d'écrire 63 et
non 64.


J'aurais plutôt tendance à faire l'inverse. Pour un single, j'aurais dit
qu'il y a un bit de signe, 8 bits d'exposant, et 24 bits de mantisse.
Pour un total de 33 bits. Sauf que puisqu'on connaît le bit de poids
fort de la mantisse, on n'a pas besoin de le stocker, et le tout passe
finalement dans 32 bits de mémoire.

Tandis que pour un extended, il y a un bit de signe, 15 bits d'exposant,
et 64 bits de mantisse, pour un total de 80 bits. Dont tous sont
physiquement stockés dans la mémoire.

précision (bits) 24 53 64

La mantisse est positive, l'exposant "biasé", c.a.d en gros signé
centré sur 0.


C-à-d en gros, non signé, mais on soustrait une valeur constante pour
en obtenir la valeur réele.


qui de ce fait devient signée. C'est du biased, quoi ... En fait, la
définition est (partiellement): "on ajoute à la valeur réelle signée
une constante telle que le résultat soit toujours positif".


C'est plutôt ta formulation que je trouve prète à confusion.
L'expression « exposant biasé » est tout à fait correcte, mais signé
centré sur 0, ça veut dire quoi. En fait, si on veut rapprocher à C++,
c'est un unsigned sur une intervalle [0;2^n[, dont on soustrait une
constant (2^(n-1)) pour avoir des valeurs réeles.

(Dans tout ceci, nous faisons tous les deux abstraction des cas
particuliers, où l'exposant a tous les bits à 0 ou tous les bits à 1.
Dans le premier cas, on ne peut plus garantir la normalisation. Du coup,
pour les single et les double, le bit de poids fort de la mantisse est
représenté, exactement comme pour les extended. dans le deuxième cas, il
s'agit des valeurs spéciales, et les bits de la mantisse ont une toute
autre sémantique -- ils indiquent le type de valeur : infini, NaN
non-signalant, NaN signalant, etc.)

Si vous faites un contrôle du total des bits, n'oubliez pas le bit
du cygne. Et il n'y a pas d'erreur, l'extended a un format un peu
particulier.


Je dirais plutôt que ce sont les float et les double qui ont un
format un peu particulier:-). C'est eux où on ne représente pas le
bit de poids fort de la mantisse (parce que dans un nombre normalisé,
il serait toujours un).


J'imagine que c'est pour le fun, votre remarque. Ne pas représenter un
bit dont on sait que la valeur est à 1 n'a rien de "particulier",
comme on voit parfois 1/2 écrit ".5" (là, c'est 0 qui est implicite).
En revanche, si vous doutez de la "particularité" de la gestion du bit
d'entier en extended, reportez-vous au data book.


Je me base sur la description du format flottant dans la norme C. En
général, tout ce qu'on peut dire d'un nombre normalisé, c'est que le
chiffre de poids fort n'est pas nul. Et en général, les flottant ne sont
pas forcément binaire : j'ai déjà travaillé sur des machines à base 16
ou à base 10, par exemple. Aussi, avant le format IEEE, il était
habituel de représenter ce bit même en base 2.

Ce qui est « particulier », évidemment, dépend du point de vue. Les
extended, chez Intel, ont la particularité de ne pas être IEEE. Mais
si on parle de la représentation en tant que telle, c'est bien les deux
autres formats qui exigent un traitement particulier : il ne suffit pas
de simplement masquer les bits pour avoir la mantisse -- il faut y en
ajouter un aussi.

Quant à ton exemple, je ne vois pas le rapport. Selon les conventions,
.5 est normalisé -- le chiffre de poids fort n'est pas null. (Il y a
deux conventions pour la normalisation, un qui met le décimal après le
premier chiffre, et le plus courant dans les représentations flottant,
qui le met avant le premier chiffre.) Ne pas représenter un chiffre
non-null, c'est une particularité qui est propre aux flottants IEEE et
leurs prédécesseurs (le format flottant des PDP-11, je crois).

Mais ce que tu documentes, c'est le hardware.
Non? Avais pas remarqué.


En fait, l'OP pose problème de précision. Ça débat autour d'un nombre
de digits significatifs. Je communique "pour info" des données qui
permettent d'accéder (à peu près) par un calcul simple à ce nombre,
pour trois formats normalisés (mais pas au sens du C++), répandus et
issus d'une FPU ayant un certain succès.


Tout ce que je disais, c'est que tes explications concernaient ce que
fait le hardware Intel. Rien n'oblige un compilateur a les utiliser.

Maintenant, je crois qu'on est tous d'accord qu'un compilateur qui
n'utilisait pas le format simple pour float, et le format double pour
double, serait un peu bizarre, pour ne pas dire complètement taré.
Personnellement, j'aurais cru que la même chose valait pour extended et
long double, avec peut-être la réservation qu'on pourrait éventuellement
pour des raisons d'alignement vouloir allouer des long double sur 16
octets, tout en ne se servant que de 10 pour sa valeur. Mais enfin, il
paraît que certains auteurs de compilateur ne sont pas d'accord avec moi
là.

Rien n'oblige un compilateur de l'utiliser, et en fait, avec VC++
6.0, j'ai sizeof(double) == sizeof(long double). Les deux vaut 8,
c-à-d donc que le compilateur utilise le type double de la FPU et
pour double et pour long double.


Ça par contre, ça me troue le c.., je n'avais jamais remarqué. C'est
pareil pour VC++7.1 bien entendu. La doc lue en diagonale ne parle
même pas d'un "specific Microsoft (x86)" qui implémenterait un mode
extended en 80 bits. Il est précisé que long double faisait 80 bits en
16-bits x86, mais est équivalent à double (64 bits) en code 32 bits,
en raison d'un choix de compatibilité RISC. Etrange.


Tout à fait mon avis. D'autant plus que les anciens MS C, sur MS-DOS 16
bits, supportait bien des long double à 80 bits. On dirait qu'il y a eu
une regression.

Quant à la compatibilité RISC : quel RISC ? Je travaille normalement sur
un Sparc (une des architectures RISC la plus repandue), et chez moi,
long double est 128 bits, avec 113 bits de mantisse, et 15 bits
d'exposant. (Et un bit de signe. Apparamment, il utilise le format IEEE
pour la mantisse, même en long double.)

À cet égard, je trouve la fonction suivante assez utile :

template< typename T >
void
info( std::ostream& dest )
{
typedef std::numeric_limits< T >
Limits ;
dest << typeid( T ).name() << ":n" ;
dest << " size = " << sizeof( T ) << 'n' ;
dest << " precision = " << Limits::digits10 << 'n' ;
dest << " max. exponent = " << Limits::max_exponent10 << 'n' ;
dest << " min. exponent = " << Limits::min_exponent10 << 'n' ;
dest << " base = " << Limits::radix << 'n' ;
dest << " base digits = " << Limits::digits << 'n' ;
dest << " base exp. max = " << Limits::max_exponent << 'n' ;
dest << " base exp. min = " << Limits::min_exponent << 'n' ;
}

Les quatre dernières lignes donne en fait le format physique, plus ou
moins. (Le nombre de bits dans l'exposant est log 2( base exp. max -
base exp. min ), arrondi à l'entier supérieur. Et on peut supposer un
bit de signe, bien que... j'en ai déjà vue un cas où il y en avait
quatre.) On my Sparc, I get:

float:
size = 4
precision = 6
max. exponent = 38
min. exponent = -37
base = 2
base digits = 24
base exp. max = 128
base exp. min = -125
double:
size = 8
precision = 15
max. exponent = 308
min. exponent = -307
base = 2
base digits = 53
base exp. max = 1024
base exp. min = -1021
long double:
size = 16
precision = 33
max. exponent = 4932
min. exponent = -4931
base = 2
base digits = 113
base exp. max = 16384
base exp. min = -16381

Pour un Intel, je me serais attendu à la même chose, à l'exception de
long double, où je me serais attendu à base digits = 64 (mais les même
valeurs pour base, base exp. max et base exp. min). À peu près le seul
autre format qu'on risque de rencontrer aujourd'hui, c'est celui des
gros IBM, avec :

float double long double
base 16 16 16
base digits 6 14 30
base exp. max 63 63 63
base exp. min -63 -63 -63

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
PRORIOL Fabien
En effet, je ne voit pas du tout l'avantage du C sur un tel problème :-P

Sinon, je vous remercie pour toute vos réponse (c'est moi l'auteur du Post,
mais j'était sur le PC d'un collegue "Cédric Billot") le jours ou j'ai
saisie ce post.
Si j'ai bien compris, mon probleme pour calculé la dérivée vient simplement
d'un problème de méthode employé.....

Donc, je vais poursuivvre ma discution sur le forum fr.comp.algorithmes qui
me semble plus approprié a ce genre de question ;-)

Encore une fois merci,
@+Fab
1 2 3