Quando utilizzare log1p ed expm1?


30

Ho una semplice domanda che è davvero difficile per Google (oltre al canonico What Every Computer Scientist dovrebbe sapere sull'aritmetica in virgola mobile ).

Quando devono essere utilizzate funzioni come log1po expm1da utilizzare anziché loge exp? Quando non dovrebbero essere usati? In che modo le diverse implementazioni di tali funzioni differiscono in termini di utilizzo?


2
Benvenuto in Scicomp.SE! Questa è una domanda molto ragionevole, ma sarebbe più facile rispondere se hai spiegato un po 'a cui log1p ti riferisci (specialmente come è implementato, quindi non dobbiamo indovinare).
Christian Clason,

4
Per argomenti a valore reale, log1p ed expm1 ( x ) dovrebbero essere usati quando x è piccolo, ad es. Quando 1 + x = 1 con precisione in virgola mobile. Vedi, ad esempio, docs.scipy.org/doc/numpy/reference/generated/numpy.expm1.html e docs.scipy.org/doc/numpy/reference/generated/numpy.log1p.html . (x)(x)x1+x=1
GoHokies,

@ChristianClason grazie, mi riferisco principalmente a C ++ std o R, ma come chiedi inizio a pensare che anche l'apprendimento delle differenze nelle implementazioni sarebbe molto interessante.
Tim


1
@ user2186862 "quando è piccolo" è corretto, ma non solo "quando 1 + x = 1 nella precisione in virgola mobile" (che accade per x 10 - 16 nell'usuale aritmetica a doppia precisione). Le pagine di documentazione che hai collegato mostrano che sono utili già per x 10 - 10 , ad esempio. x1+x=1x1016x1010
Federico Poloni,

Risposte:


25

Sappiamo tutti che

exp(x)=n=0xnn!=1+x+12x2+
implica che per|x|1, abbiamoexp(x)1+x. Ciò significa che se dobbiamo valutare in virgola mobileexp(x)1, per|x|1cancellazione catastrofica può verificarsi.

Questo può essere facilmente dimostrato in 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

I valori esatti sono

exp(108)1=0.000000010000000050000000166666667083333334166666668exp(1022)1=0.000000000000000000000100000000000000000000005000000

In generale, un'implementazione "accurata" di expe expm1dovrebbe essere corretta per non più di 1ULP (cioè un'unità dell'ultimo posto). Tuttavia, poiché il raggiungimento di questa accuratezza comporta un codice "lento", a volte è disponibile un'implementazione rapida e meno accurata. Ad esempio in CUDA abbiamo expfe expm1f, dove fsta veloce. Secondo la guida alla programmazione CUDA C, app. D l' expfha un errore di 2ULP.

Se non ti preoccupi degli errori nell'ordine di pochi ULPS, di solito diverse implementazioni della funzione esponenziale sono equivalenti, ma fai attenzione che i bug potrebbero essere nascosti da qualche parte ... (Ricorda il bug Pentium FDIV ?)

expm1exp(x)1xxexpm1

>>> exp(200)-1 == exp(200) == expm1(200)
True

1exp(200)

loglog1plog(1+x)x|x|1


1
Questa risposta era già contenuta nei commenti alla domanda del PO. Tuttavia mi sono sentito utile dare un resoconto più lungo (anche se di base) solo per chiarezza, nella speranza che possa essere utile ad alcuni lettori.
Stefano M,

OK, ma poi si può semplicemente concludere "così posso sempre usare expm1 invece di exp" ...
Tim

1
@tim la tua conclusione è sbagliata: puoi sempre usare al expm1(x)posto di exp(x)-1. Naturalmente exp(x) == exp(x) - 1non vale in generale.
Stefano M,

x1

1
expm1(x)0x1exp(x) - 1x1x<ϵϵ

1

Per espandere la differenza tra loge log1ppotrebbe essere utile richiamare il grafico se il logaritmo:

Logaritmo

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

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

1log01log1p

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.