Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pourquoi 10^16+1-10^16 = 0 ?

59 views
Skip to first unread message

ZarkXe

unread,
Oct 14, 2012, 2:51:41 PM10/14/12
to
Bonjour à tous,

Je du mal a comprend pourquoi 10^16 + 1 - 10^16 = 0 ?

Merci d'avance.

ZarkXe

---------------%<-------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main() {
double total3 = pow(10, 16) + 1 - pow(10, 16);
printf("pow(16) + 1 - pow(16) = %lf\n", total3);

return EXIT_SUCCESS;
}
---------------%<-------------

Éric Lévénez

unread,
Oct 14, 2012, 3:21:41 PM10/14/12
to
Le 14/10/12 20:51, ZarkXe a écrit :
Le double contient un flottant avec mantisse et exposant. La taille de
la mantisse ne peut contenir la valeur exacte de 10^16 + 1 et donc le +1
est en quelque sorte ignoré.

Pour avoir plus de chiffres significatifs, il faut passer à un truc du
genre :

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
long double total3 = powl(10, 16) + 1 - powl(10, 16);
printf("pow(16) + 1 - pow(16) = %Lf\n", total3);

return EXIT_SUCCESS;
}



--
Éric Lévénez
FAQ de fclc : <http://www.levenez.com/lang/c/faq/>

Samuel DEVULDER

unread,
Oct 14, 2012, 3:48:17 PM10/14/12
to
Le 14/10/2012 20:51, ZarkXe a écrit :
> Bonjour à tous,
>
> Je du mal a comprend pourquoi 10^16 + 1 - 10^16 = 0 ?

Parce que le double n'a pas assez de précision pour représenter
exactement 10^16+1. En effet la mantisse ayant 52bits, on peut
représenter exactement les 53 premiers bits des valeurs numériques (1 de
plus à cause du bit de poids fort implicite). Or 2^53=9E15, un peu plus
petit que 10^16. Donc en mémoire, au format IEEE753, (10^16+1) et
(10^16) sont indiscernables. Leur différence fait donc 0.

Pour éviter ce défaut, il vaut mieux réorganiser les équations pour
additionner/soustraire les termes par ordre de grandeur décroissante.
Ainsi si tu avais fait 10^16 - 10^16 + 1 tu aurais obtenu 1, car (10^16
- 10^16) aurait été évalué en 1er donnant 0, lequel additionné à 1 donne
bien 1.

sam.

Lucas Levrel

unread,
Oct 15, 2012, 7:55:23 AM10/15/12
to
Le 14 octobre 2012, Samuel DEVULDER a écrit :

> Pour éviter ce défaut, il vaut mieux réorganiser les équations pour
> additionner/soustraire les termes par ordre de grandeur décroissante.

À voir selon le cas. Dans un fil récent, il s'agissait de calculer la
somme des 1/k avec k entier. Si on commence par les plus gros (k
croissant), au bout d'un moment les additions ne font plus rien (les
termes deviennent individuellement négligeables).

Ce qu'il faut faire, me semble-t-il, c'est regrouper les termes de
grandeur comparable. Et se méfier des soustractions !

--
LL

Alexandre Bacquart

unread,
Oct 15, 2012, 8:23:51 AM10/15/12
to
On 10/14/2012 09:48 PM, Samuel DEVULDER wrote:
> Le 14/10/2012 20:51, ZarkXe a écrit :
>> Bonjour à tous,
>>
>> Je du mal a comprend pourquoi 10^16 + 1 - 10^16 = 0 ?
>
> Parce que le double n'a pas assez de précision pour représenter
> exactement 10^16+1.

Strictement parlant, il n'en a d'ailleurs même pas assez pour
représenter exactement 2 ;)

Les propriétés des flottants font qu'ajouter 1 à une valeur aussi grande
que 10^16 n'aura vraisemblablement aucun effet (ni float ni double ne
sont assez précis).

Le problème peut être résolu de plusieurs manières :

1. Méthode brute : http://gmplib.org/

2. Méthode old-school : utiliser des entiers. Si besoin de fractions,
faire de la virgule fixe (fixed point). Imparable, mais pour des valeurs
de l'ordre de 10^16, 64 bits est un minimum.

3. Méthode hacker sûr de lui : isoler les calculs flottants sur des
petites valeurs et ceux sur des grandes valeurs. Après, le tout est de
définir petites et grandes valeurs. Et il vaut mieux avoir une très
bonne notion de ce qu'on fait.


--
Alexandre

Marc Boyer

unread,
Oct 15, 2012, 10:06:45 AM10/15/12
to
Faut surtout garder en tête que l'analyse numérique est un job
à part, et que tant qu'il s'agit de calculer des trucs pour jouer,
on peut s'amuser, mais dès que ça devient sérieux, il faut faire
très attention.

On rapporte toujours l'exemple d'un logiciel de compta
dont les sommes HT + TVA != TTC.

Marc Boyer
--
À mesure que les inégalités regressent, les attentes se renforcent.
François Dubet

Antoine Leca

unread,
Oct 15, 2012, 1:01:50 PM10/15/12
to
Marc Boyer écrivit :
> Faut surtout garder en tête que l'analyse numérique est un job
> à part, [...] ça devient sérieux, il faut faire très attention.
>
> On rapporte toujours l'exemple d'un logiciel de compta
> dont les sommes HT + TVA != TTC.

Et le rapport avec soit le langage C, soit l'analyse numérique est ?


Antoine

Antoine Leca

unread,
Oct 15, 2012, 1:18:31 PM10/15/12
to
Alexandre Bacquart écrivit :
> Les propriétés des flottants font qu'ajouter 1 à une valeur aussi grande
> que 10^16 n'aura vraisemblablement aucun effet (ni float ni double ne
> sont assez précis).

Bzzz ! Je soupçonne une dépendance cachée avec ta machine...


> Le problème peut être résolu de plusieurs manières :
>
> 1. Méthode brute : http://gmplib.org/
>
> 2. Méthode old-school : utiliser des entiers. Si besoin de fractions,
> faire de la virgule fixe (fixed point). Imparable, mais pour des valeurs
> de l'ordre de 10^16, 64 bits est un minimum.
>
> 3. Méthode hacker sûr de lui : isoler les calculs flottants sur des
> petites valeurs et ceux sur des grandes valeurs. Après, le tout est de
> définir petites et grandes valeurs. Et il vaut mieux avoir une très
> bonne notion de ce qu'on fait.

4. méthode gros bourrin (qui vaut exactement autant que la remarque
ci-dessus) : regardez un poil la documentation du compilateur, remarquer
que LDBL_DIG y vaut 19 ou 30 ce qui garantit que les opérations
utilisent plus de 16 chiffres décimaux significatifs, et utiliser des
long doubles...

5. méthode le-gars-des-années-60 : utiliser des flottants "double float"
précision doublée fait-maison c'est-à-dire struct {double d[2];}
Évidemment toutes les opérations doivent être programmées une par une ;
par rapport à GMP dont cette méthode est l'ancêtre, c'est un poil plus
rapide en général mais est limité à (environ) 30 chiffres décimaux.


Antoine

Samuel DEVULDER

unread,
Oct 15, 2012, 5:09:05 PM10/15/12
to
Le 15/10/2012 14:23, Alexandre Bacquart a écrit :
> On 10/14/2012 09:48 PM, Samuel DEVULDER wrote:
>> Le 14/10/2012 20:51, ZarkXe a écrit :
>>> Bonjour à tous,
>>>
>>> Je du mal a comprend pourquoi 10^16 + 1 - 10^16 = 0 ?
>>
>> Parce que le double n'a pas assez de précision pour représenter
>> exactement 10^16+1.
>
> Strictement parlant, il n'en a d'ailleurs même pas assez pour
> représenter exactement 2 ;)

Je ne comprends pas. Typiquement 2 sera représenté par les bits suivants
sur un float 32bit ieee 754:

0 10000000 00000000000000000000000
s <-expo-> <------mantisse------->
1 <7 bits> <----- 22 bits ------->

value = (1-2*s)*(1+mantisse/2^23)*2^(expo-127)

Dans l'exemple s=0, mantisse=0, expo=128, donc value=2 exactement.

Plus généralement parlant tous les entiers de 0 à 2^23-1 peuvent être
représentés et ajoutés ou soustraits sans erreur sur un float32 (la
mantisse contient assez de bits pour cela), et jusqu'à 2^53-1 sur un
float64. (je ne compte pas les nombres dénormalisés).

Un bon exemple pour jouer avec le format IEEE est de coder une
implémentation d'une représentation minifloat 8bits 1.4.3
(http://en.wikipedia.org/wiki/Minifloat). C'est très marrant, il n'y a
que 242 valeurs possibles mais elles s'étalent de -122880 à 122880, soit
une amplitude plus grande que 16bits. On peut y représenter exactement
tous les entiers de -16 à +16. C'est pas mal avec si peu de bits!

> Les propriétés des flottants font qu'ajouter 1 à une valeur aussi grande
> que 10^16 n'aura vraisemblablement aucun effet (ni float ni double ne
> sont assez précis).

Oui, en plus je pense le résultat dépend du mode d'arrondi de l'unité
flottante ou si elle travaille sur un format étendu en interne ou pas.

sam.

Marc Boyer

unread,
Oct 16, 2012, 9:57:40 AM10/16/12
to
Ben, que (x) et (x * (1-e)) + (x*e) ne sont pas garantis comme égaux
en flottant, même pour des valeurs asez "assez peu" de chiffres
significatifs (une TVA, c'est 4 chiffres significatifs et une facture
chez le commun des mortel dépasse rarement les 10 chiffres).

Antoine Leca

unread,
Oct 17, 2012, 4:02:12 AM10/17/12
to
Marc Boyer écrivit :
> Le 15-10-2012, Antoine Leca <ro...@localhost.invalid> a écrit :
>> Marc Boyer écrivit :
>>> Faut surtout garder en tête que l'analyse numérique est un job
>>> à part, [...] ça devient sérieux, il faut faire très attention.
>>>
>>> On rapporte toujours l'exemple d'un logiciel de compta
>>> dont les sommes HT + TVA != TTC.
>>
>> Et le rapport avec soit le langage C, soit l'analyse numérique est ?
>
> Ben, que (x) et (x * (1-e)) + (x*e) ne sont pas garantis comme égaux
> en flottant, même pour des valeurs asez "assez peu" de chiffres
> significatifs (une TVA, c'est 4 chiffres significatifs et une facture
> chez le commun des mortel dépasse rarement les 10 chiffres).

On est hors sujet mais je continue un peu : il me paraît évident (depuis
de nombreuses années, donc avant de programmer en C) que les
applications de comptabilité ne doivent pas manipuler les montants en
utilisant directement les types flottants, parce que les chiffres
décimaux après la virgule n'ont pas de représentation unique.

Comme en C il n'y a/avait pas de virgule fixe, cela ne laissait que les
type entiers ; autrement dit en C89 au maximum 7+2 chiffres
significatifs ce qui était insuffisant. D'où l'idée proposée par
certains d'utiliser le type double qui garantit 15 chiffres, suffisant
en pratique en comptabilité *si on prend garde à calculer en centimes*
(ou en millimes voire décimillimes). Les doubles ne sont alors utilisée
que comme des entiers avec une étendue de 53 bits (cf. le message de
Samuel); évidemment depuis C99 cela n'est plus nécessaire (à supposer
que beaucoup de fous ont programmé de la compta en C).

À propos du nombre de chiffres, un total de bilan en pesètes ou en lires
atteint sans difficulté 11 chiffres avant la virgule... C'est vrai
qu'avec l'euro on est revenu avec des sommes plus facile à manipuler,
mais il existe toujours des pays où on peut avoir besoin de plus de
chiffres (un milliard de yens ou de francs CFA/CFP n'est pas une facture
irréaliste).

À propos de calcul de TVA, pour moi la TVA est toujours en centimes
(même si la manière d'arrondir peut être l'objet de discussions
«intéressantes» avec le fisc...) Par contre, la formule HT + TVA != TTC
peut apparaître quand tu dois recalculer le montant HT à partir du TTC:
il est des fois où cela ne tombe pas juste !


Antoine

Dominique MICOLLET

unread,
Oct 17, 2012, 6:43:50 AM10/17/12
to
Antoine Leca a écrit :

> On est hors sujet mais je continue un peu : il me paraît évident (depuis
> de nombreuses années, donc avant de programmer en C) que les
> applications de comptabilité ne doivent pas manipuler les montants en
> utilisant directement les types flottants, parce que les chiffres
> décimaux après la virgule n'ont pas de représentation unique.

Pour avoir enseigné un peu d'informatique de gestion, programmée
essentiellement en COBOL, je peux confirmer que l'unité est le centime et
non l'euro (ou plutôt le franc à l'époque).
D'ailleurs les calculatrices comptables avaient (ont ?) une touche "00"
permettant d'entrer un nombre "entier" 5,00 => "5" "00".
C'est bien de la virgule fixe, plus simple à mettre en oeuvre au niveau des
processeurs. C'est la procédure d'affichage qui se débrouille pour remettre
la virgule au bon endroit.

> À propos de calcul de TVA, pour moi la TVA est toujours en centimes
> (même si la manière d'arrondir peut être l'objet de discussions
> «intéressantes» avec le fisc...) Par contre, la formule HT + TVA != TTC
> peut apparaître quand tu dois recalculer le montant HT à partir du TTC:
> il est des fois où cela ne tombe pas juste !

Le problème de la TVA et d'un certain nombre d'autres taxes est
effectivement lié aux arrondis, car les taux ne fournissent pas des portions
centimes : il faut ruser pour les récupérer ou les cacher. Et pour autant
que je sache, ils se calculent sur le montant TTC.

Je serais curieux de connaitre la règle d'arrondi du fisc. Wikipedia
mentionne un "arrondi bancaire" sans expliquer son intérêt.

Cordialement

Dominique.

Dominique MICOLLET

unread,
Oct 17, 2012, 6:45:25 AM10/17/12
to
Dominique MICOLLET a écrit :

Un truc illisible :
> effectivement lié aux arrondis, car les taux ne fournissent pas des
> portions centimes : il faut ruser pour les récupérer ou les cacher. Et

... car les taux fournissent des portions de centimes ....

Olivier Miakinen

unread,
Oct 17, 2012, 8:38:50 AM10/17/12
to
Le 17/10/2012 12:43, Dominique MICOLLET a écrit :
>
> Wikipedia mentionne un "arrondi bancaire" sans expliquer son intérêt.

http://fr.wikipedia.org/wiki/Arrondi#M.C3.A9thode_d.27arrondissage_au_pair_le_plus_proche

À ce que je suppose, l'intérêt est d'arrondir une fois sur deux vers le
haut et une fois sur deux vers le bas, ce qui fait qu'en moyenne les
biais auront plus de chances de se compenser que de s'accumuler.

Je suppose aussi qu'il reviendrait au même d'arrondir toujours vers le
chiffre impair plutôt que toujours vers le chiffre pair.

Cordialement,
--
Olivier Miakinen

Samuel DEVULDER

unread,
Oct 17, 2012, 2:34:00 PM10/17/12
to
Le 17/10/2012 12:43, Dominique MICOLLET a écrit :


> Je serais curieux de connaitre la règle d'arrondi du fisc. Wikipedia
> mentionne un "arrondi bancaire" sans expliquer son intérêt.

Je crois que l'arrondi du banquier est arrondir à l'entier pair le plus
proche (round to even):

1.5 est arrondi à 2
2.5 est arrondi à 2
3.5 est arrondi à 4

-1.5 est arrondi à -2
-2.5 est arrondi à -2
-3.5 est arrondi à -4

L’intérêt est que cela limite les biais d'arrondis quand on enchaine les
opérations "arrondies" les unes aux autres. C'est le mode d'arrondi par
défaut dans IEEE754 d'après la wikipedia.

sam.

Antoine Leca

unread,
Oct 18, 2012, 1:28:35 PM10/18/12
to
Samuel DEVULDER écrivit :
> Le 17/10/2012 12:43, Dominique MICOLLET a écrit :
>
>> Je serais curieux de connaitre la règle d'arrondi du fisc. Wikipedia
>> mentionne un "arrondi bancaire" sans expliquer son intérêt.
>
> Je crois que l'arrondi du banquier est arrondir à l'entier pair le plus
> proche (round to even):

... uniquement dans le cas de strict égalité de distance.

> 1.5 est arrondi à 2
> 2.5 est arrondi à 2
> 3.5 est arrondi à 4

1.51 arrondi à 2
2.51 arrondi à 3
2.50000000000000000000000000000000000000000000000000000001 arrondi à 3


> L’intérêt est que cela limite les biais d'arrondis quand on enchaine les
> opérations "arrondies" les unes aux autres.

Je ne crois que le souci soient les enchaînements, mais plutôt le biais
systématique pour la valeur .500000_0_ (et seulement elle), biais qui
est vers le haut¹ si on arrondi de manière habituelle (par défaut pour
la première décimale entre 0 et 4, par excès pour la première décimale
entre 5 et 9).


Antoine
__________
¹: en fait, le biais est vers l'infini, ou encore "away of zero".

Samuel DEVULDER

unread,
Oct 18, 2012, 4:59:32 PM10/18/12
to
Le 18/10/2012 19:28, Antoine Leca a écrit :
> Samuel DEVULDER écrivit :
>> Je crois que l'arrondi du banquier est arrondir à l'entier pair le plus
>> proche (round to even):
>
> ... uniquement dans le cas de strict égalité de distance.

Oui j'avais oublié de le préciser. Sans cela on a un sacré biais: le
résultat est pair dans 100% des cas!

>> 1.5 est arrondi à 2
>> 2.5 est arrondi à 2
>> 3.5 est arrondi à 4
>
> 1.51 arrondi à 2
> 2.51 arrondi à 3

Oui. Ce sont des arrondis au plus proche.

> 2.50000000000000000000000000000000000000000000000000000001 arrondi à 3

Dans l'esprit oui. Il est plus proche de 3 que de 2. Cependant ce nombre
n'est pas représentable en double ou float. Du coup son représentant le
plus proche: 2.5000000000000000 sera lui arrondi à 2.

>> L’intérêt est que cela limite les biais d'arrondis quand on enchaine les
>> opérations "arrondies" les unes aux autres.
>
> Je ne crois que le souci soient les enchaînements, mais plutôt le biais
> systématique pour la valeur .500000_0_ (et seulement elle), biais qui

en effet.

> est vers le haut¹ si on arrondi de manière habituelle (par défaut pour
> la première décimale entre 0 et 4, par excès pour la première décimale
> entre 5 et 9).

Il a aussi d'autres propriétés. Comme il est symétrique par rapport à 0,
il commute avec la négation: arrondi(-x) = -arrondi(x). Je crois aussi
qu'il préserve la moyenne pour des distributions suffisamment éloignées
de 0: moyenne(arrondi(x1), arrondi(x2), ...) = moyenne(x1, x2, ...).
J'imagine que cela doit être lié à l'absence de biais qui ne s'accumule
pas dans la sommation nécessaire à la moyenne.

Merci pour toutes les précisions,

sam.
0 new messages