Quand utiliser log1p et expm1?


30

J'ai une question simple qui est vraiment difficile pour Google (à part le canonique que tout informaticien devrait savoir sur le papier arithmétique à virgule flottante ).

Quand faut log1p- expm1il utiliser des fonctions telles que ou au lieu de loget exp? Quand ne devraient-ils pas être utilisés? Comment les différentes implémentations de ces fonctions diffèrent-elles en termes d'utilisation?


2
Bienvenue sur Scicomp.SE! C'est une question très raisonnable, mais il serait plus facile de répondre si vous expliquiez un peu ce log1p à quoi vous faites référence (en particulier comment il est mis en œuvre, donc nous n'avons pas à deviner).
Christian Clason

4
Pour les arguments à valeur réelle, log1p et expm1 ( x ) doivent être utilisés lorsque x est petit, par exemple lorsque 1 + x = 1 en précision à virgule flottante. Voir, par exemple, docs.scipy.org/doc/numpy/reference/generated/numpy.expm1.html et docs.scipy.org/doc/numpy/reference/generated/numpy.log1p.html . (x)(x)x1+x=1
GoHokies

@ChristianClason merci, je me réfère principalement à C ++ std ou R, mais comme vous le demandez, je commence à penser que l'apprentissage des différences dans les implémentations serait également très intéressant.
Tim


1
@ user2186862 "lorsque est petit" est correct, mais pas seulement "lorsque 1 + x = 1 en précision à virgule flottante" (ce qui se produit pour x 10 - 16 dans l'arithmétique habituelle à double précision). Les pages de documentation que vous avez liées montrent qu'elles sont déjà utiles pour x 10 - 10 , par exemple. x1+x=1x1016x1010
Federico Poloni du

Réponses:


25

Nous savons tous que

exp(x)=n=0xnn!=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(108)1=0.000000010000000050000000166666667083333334166666668exp(1022)1=0.000000000000000000000100000000000000000000005000000

En général, une mise en œuvre «précise» de expet expm1devrait ê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 expfet expm1f, où fsignifie rapide. Selon le guide de programmation CUDA C, app. D l' expfa 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 expm1devrait ê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 expm1devrait ê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 loget log1ppuisque log(1+x)x pour |x|1 .


1
Cette réponse figurait déjà dans les commentaires sur la question du PO. Cependant, je me suis senti utile de donner un compte rendu plus long (bien que basique) juste pour plus de clarté, dans l'espoir qu'il sera utile à certains lecteurs.
Stefano M

OK, mais alors on peut simplement conclure "donc je peux toujours utiliser expm1 au lieu de exp" ...
Tim

1
@tim votre conclusion est fausse: vous pouvez toujours utiliser à la expm1(x)place de exp(x)-1. Bien sûr, exp(x) == exp(x) - 1ne tient pas en général.
Stefano M

OK, c'est clair. Et existe-t-il des critères clairs pour ? x1
Tim

1
@Tim il n'y a pas de seuil précis et la réponse dépend de la précision de l'implémentation en virgule flottante (et du problème résolu). Alors qu'il expm1(x)devrait être précis à 1ULP sur toute la plage , il perd progressivement la précision de quelques ULP lorsque x 1 à une panne complète lorsque x < ϵ , où ϵ est epsilon machine. 0x1exp(x) - 1x1x<ϵϵ
Stefano M

1

Pour développer la différence entre loget log1pil peut être utile de rappeler le graphique si le logarithme:

Logarithm

logx0ln(x)x0ln(x)ln(1e)=1ln(1e10)=10

x0ln(x+1)0ln(1+1e)0.31ln(1+1e10)0.000045log1p

1log01log1p

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.