(Partiellement résolu) [C] Problèmes de transtypage float -> int

Salut à tous!

J’ai un petit problème de transtypage float -> int en C et je ne comprends pas trop d’où il peut venir.

Voici un programme simple illustrant mon problème:[code]#include <stdio.h>

int main()
{
int a;
float b, c, r;

a = 7;
b = 4.0 / 9.0;
c = 6.0 + 1.0 / 9.0;

r = ((a - c) / b);

printf("%f\n%d\n", r, (int)r);

return 0;

}[/code]

Et voici le retour du printf lors de l’exécution:[quote]2.000000
1[/quote]

Donc la variable r vaut 2.000000 mais sa partie entière vaut 1, et non pas 2, d’après ce programme.

Bon c’est un cas particulier, dans mon programme ce genre de cas se produisent rarement, mais ce produisent quand même, ce qui pose des problèmes. Ici je vous ai isolé un de ces cas particuliers qui me posent problème, seulement je ne comprend pas trop pourquoi.

Quelqu’un pourrait-il m’éclairer?

Comment puis-je contourner le problème simplement?

Ah ouais d’accord, en fait le printf ne m’affiche pas toutes les décimales.

En réalité si j’en affiche plus j’ai 1.9999998808 par exemple. Je pensais que printf m’affichait toutes les décimales.

En prog, 1+1=3 si 1 est suffisamment proche de 2 et 3 suffisamment proche de 2

A méditer.

[code]#include

using namespace std;

int main()
{
int a;
float b, c, r;

a = 7;
b = 4.0 / 9.0;
c = 6.0 + 1.0 / 9.0;

cout << " a : " << a
	<< "\n b : " << b
	<< "\n c : " << c;

r = ((a - c) / b);

cout << "\nR : " << r << '\n'
	<< "(int)r : " << int(r)
	<< hex
	<< "\n0x" << *((int*)&r)
	<< "\n0x" << int(r) << endl;

return 0;

}[/code]
juste rajouté des affichages (passé en C++ pour pas me prendre la tête), et le résultat est intéressant :

a : 7 b : 0.444444 c : 6.11111 R : 2 (int)r : 1 0x3fffffff 0x1
Donc à vue de pif ton flotant doit valoir 1,999999 ou un truc du genre, et il a été arrondi par la traduction en texte. Si il faisait exactement 2 y’aurait un paquet de 0 dans la mantisse normalement, hors là tous les bits sont à 1 :

[code]0011 1111 1111 1111 1111 1111 1111 1111

signe : 0 -> positif
exposant : 01111111 : 127 nickel, ça veut dire que le nombre est * 2^0
mantisse : que du 1 -> donc vu que la partie entière est implicite et est à 1
toutes les décimales sont à 1[/code]

edit : grilled

meme conclusion pour moi, je viens de tester aussi B)

A noter que tout en double, t’as plus de precision et la conversion en INT vaut 2 elle aussi.

LoneWolf
Amusant pour se reveiller B)

En double la conversion en int vaut 2 dans le cas présent, oui B)

Mais j’aurais encore certainement des erreurs dans d’autres cas.

Bref, j’ai du faire une bêtise dans une fonction de calculs dans mon programme, ce qui fait qu’elle me sort quelque chose légèrement inférieur à la valeur réelle, allez savoir pourquoi. Je cherche, si je trouve pas, je posterai la fonction posant problème B)

Bah c’est un problème d’arrondis. Prends une bête calculatrice, divise 1 par 3, et tu obtiens un nombre avec un développement infini 0,333333… etc. Le problème est que l’infini en question va être tronqué en fonction des capacités de la machine, et par exemple ça va ne stocker que 8 chiffres 0,33333333. Si tu remultiplies par 3, tu obtiens 0,99999999. “(1 / 3) * 3 <> 1” si on travaille en précision finie. Alors oui, la plupart du temps, on récupère le coup en considérant qu’un truc suffisamment proche d’une certaine valeur est la valeur en question (ce qui explique le résultat avec double). Mais ça reste incorrect.

Ce qui est important à réaliser aussi, c’est qu’un nombre qui a un développement fini en décimal n’a pas forcément un développement fini en binaire. Par exemple, on peut facilement représenter 1 / 100 en décimal (0,01) mais en binaire c’est infini. Essaie d’additionner 100 fois 0,01, et tu n’obtiendra pas 1, car la réprésentation de 0,01 en binaire est une valeur approximative.

Pour référence, voici comment les ordinateurs stockent les nombres à virgule flottante : http://en.wikipedia.org/wiki/IEEE_754

La solution réside dans le remaniement de tes calculs pour éviter autant que possible ces problèmes d’arrondis, ou au mieux les minimiser.

Attention ça pique B) :
http://docs.sun.com/source/806-3568/ncg_goldberg.html

Pour en avoir lu une bonne partie je vous assure que cette doc ouvre les yeux sur les tas de problèmes insoupsonnés (ou pas) avec les flottants.

[quote=“JDaM, post:8, topic: 32190”]Attention ça pique B) :
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Pour en avoir lu une bonne partie je vous assure que cette doc ouvre les yeux sur les tas de problèmes insoupsonnés (ou pas) avec les flottants.[/quote]
Il a l’air très intéressant cet article, je l’ajoute immédiatement à ma pile de “trucs à lire d’urgence”, merci JDaM !

Je viens d’essayer de trouver une illustration sur les erreurs absolues dans la représentation des entiers, j’en ai déjà vu, mais je ne retrouve plus. En fait l’idée la plus impotante à ce niveau, c’est qu’un entier perd en précision absolue au fur et à mesure qu’on s’éloigne de zéro, étant donné le principe mantisse / exposant. Ceci dit, j’ai trouvé un article et des illustrations sur les erreurs relatives, et c’est un joyeux bordel aussi : http://www.ece.uwaterloo.ca/~ece104/TheBoo…loat/error.html

Je devrais pouvoir faire un petit programme qui génère un graphe mettant en évidence les erreurs absolues. Je vais voir si j’en trouve le temps.

Merci pour ces liens B)