J'essaie de calculer une approximation de la valeur epsilon pour le type float
(et je sais que c'est déjà dans la bibliothèque standard).Arithmétique virgule flottante et machine epsilon
Les valeurs de epsilon sur cette machine sont (imprimées avec une certaine approximation):
FLT_EPSILON = 1.192093e-07
DBL_EPSILON = 2.220446e-16
LDBL_EPSILON = 1.084202e-19
FLT_EVAL_METHOD
est 2
si tout est fait en long double
précision, et float
, double
et long double
sont 32, 64 et 96 bits.
J'ai essayé d'obtenir une approximation de la valeur à partir de 1 et en le divisant par deux jusqu'à ce qu'il devienne trop petit, faire toutes les opérations avec le type float
:
# include <stdio.h>
int main(void)
{
float floatEps = 1;
while (1 + floatEps/2 != 1)
floatEps /= 2;
printf("float eps = %e\n", floatEps);
}
La sortie n'est pas ce que je recherchais pour:
float epsilon = 1.084202e-19
opérations intermédiaires sont faites avec la plus grande précision (en raison de la valeur de FLT_EVAL_METHOD
), de sorte que ce résultat semble légitime.
Cependant, ceci:
// 2.0 is a double literal
while ((float) (1 + floatEps/2.0) != 1)
floatEps /= 2;
donne cette sortie, ce qui est la bonne:
float epsilon = 1.192093e-07
mais celui-ci:
// no double literals
while ((float) (1 + floatEps/2) != 1)
floatEps /= 2;
conduit à nouveau à un mauvais résultat, comme le premier:
float epsilon = 1.084202e-19
Ces deux dernières versions devraient être équivalentes sur cette plateforme, est-ce un bug de compilateur? Si non, que se passe-t-il?
codeest compilé avec:
gcc -O0 -std=c99 -pedantic file.c
La version gcc est assez vieux, mais je suis à l'université et je ne peux pas le mettre à jour:
$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8'
--with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs
--enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4
--enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib
--libexecdir=/usr/lib --without-included-gettext --enable-threads=posix
--with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls
--enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc
--enable-targets=all --with-arch-32=i586 --with-tune=generic
--enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu
--target=i486-linux-gnu
Thread model: posix
gcc version 4.4.5 (Debian 4.4.5-8)
La version actuelle de gcc, 4.7 , se comporte correctement sur mon ordinateur personnel. Il y a aussi des commentaires disant que différentes versions donnent des résultats différents. Après quelques réponses et commentaires, qui ont clarifié ce qui se passe comme prévu et ce qui ne l'est pas, j'ai légèrement changé la question pour la rendre plus claire.
Quel compilateur utilisez-vous, et quels commutateurs utilisez-vous (cible, paramètres d'optimisation, et cetera)? Etes-vous positif que le code avec '(float) (1 + floatEps/2.)' et le code avec '(float) (1 + floatEps/2)' et aucune autre différence ne donne des résultats différents? Les deux fichiers source diffèrent uniquement dans ce seul caractère, et exactement les mêmes commutateurs de compilateur ont été utilisés? –
@EricPostpischil bien, un ancien, j'ai ajouté des détails. Quoi qu'il en soit, j'ai testé le code à nouveau et je confirme les résultats. – effeffe
Impossible de reproduire avec gcc 4.6.2. Le cast à 'float' produit la valeur correcte (' FLT_EVAL_METHOD' est 2, x86) pour les deux versions. –