Nous savons tous que
exp(x)=∑n=0∞xnn!=1+x+12x2+…
implique que pour|x|≪1, on aexp(x)≈1+x. Cela signifie que si nous devons évaluer en virgule flottanteexp(x)−1, pour|x|≪1annulation catastrophique peut se produire.
Cela peut être facilement démontré en python:
>>> from math import (exp, expm1)
>>> x = 1e-8
>>> exp(x) - 1
9.99999993922529e-09
>>> expm1(x)
1.0000000050000001e-08
>>> x = 1e-22
>>> exp(x) - 1
0.0
>>> expm1(x)
1e-22
Les valeurs exactes sont
exp(10−8)−1exp(10−22)−1=0.000000010000000050000000166666667083333334166666668…=0.000000000000000000000100000000000000000000005000000…
En général, une mise en œuvre «précise» de exp
et expm1
devrait être correcte à pas plus de 1ULP (c'est-à-dire une unité de la dernière place). Cependant, étant donné que l'atteinte de cette précision entraîne un code "lent", une implémentation rapide et moins précise est parfois disponible. Par exemple, dans CUDA, nous avons expf
et expm1f
, où f
signifie rapide. Selon le guide de programmation CUDA C, app. D l' expf
a une erreur de 2ULP.
Si vous ne vous souciez pas des erreurs de l'ordre de quelques ULPS, généralement différentes implémentations de la fonction exponentielle sont équivalentes, mais attention aux bogues qui peuvent être cachés quelque part ... (Rappelez-vous le bogue Pentium FDIV ?)
Il est donc assez clair que cela expm1
devrait être utilisé pour calculer exp(x)−1 pour les petits x . Son utilisation pour le x général n'est pas nocive, car elle expm1
devrait être précise sur toute sa plage:
>>> exp(200)-1 == exp(200) == expm1(200)
True
(Dans l'exemple ci-dessus 1 est bien en dessous de 1ULP deexp(200) , donc les trois expressions renvoient exactement le même nombre à virgule flottante.)
Une discussion similaire vaut pour les fonctions inverses log
et log1p
puisque log(1+x)≈x pour |x|≪1 .
log1p
à quoi vous faites référence (en particulier comment il est mis en œuvre, donc nous n'avons pas à deviner).