Tengo una pregunta simple que es realmente difícil para Google (además del canónico Lo que todo informático debe saber sobre el papel de aritmética de punto flotante ).
¿Cuándo deberían usarse funciones como log1p
o en expm1
lugar de log
y exp
? ¿Cuándo no deberían ser utilizados? ¿Cómo difieren las diferentes implementaciones de esas funciones en términos de su uso?
floating-point
Tim
fuente
fuente
log1p
se refiere (especialmente cómo se implementa, por lo que no tenemos que adivinar).Respuestas:
Todos sabemos que implica que para , tenemos . Esto significa que si tenemos que evaluar en coma flotante , para puede producir una cancelación catastrófica.exp(x)=∑n=0∞xnn!=1+x+12x2+… |x|≪1 exp(x)≈1+x exp(x)−1 |x|≪1
Esto se puede demostrar fácilmente en python:
Los valores exactos sonexp(10−8)−1exp(10−22)−1=0.000000010000000050000000166666667083333334166666668…=0.000000000000000000000100000000000000000000005000000…
En general, una implementación "precisa" de
exp
yexpm1
debe ser correcta a no más de 1ULP (es decir, una unidad del último lugar). Sin embargo, dado que lograr esta precisión da como resultado un código "lento", a veces hay disponible una implementación rápida y menos precisa. Por ejemplo en CUDA tenemosexpf
yexpm1f
, dondef
significa rápido. De acuerdo con la guía de programación CUDA C, aplicación. D elexpf
tiene un error de 2ULP.Si no le importan los errores en el orden de pocas ULPS, generalmente las implementaciones diferentes de la función exponencial son equivalentes, pero tenga en cuenta que los errores pueden estar ocultos en alguna parte ... (¿Recuerda el error Pentium FDIV ?)
Por lo tanto, está bastante claro queexp(x)−1 x x
expm1
debería usarse para calcular para pequeña . Usarlo para general no es dañino, ya que se espera que sea preciso en todo su rango:expm1
(En el ejemplo anterior está muy por debajo de 1ULP de , por lo que las tres expresiones devuelven exactamente el mismo número de coma flotante).1 exp(200)
Una discusión similar es válida para las funciones inversaslog(1+x)≈x |x|≪1
log
ylog1p
desde para .fuente
expm1(x)
lugar deexp(x)-1
. Por supuesto queexp(x) == exp(x) - 1
no se cumple en general.expm1(x)
debe ser preciso a 1ULP en todo el rango , pierde progresivamente la precisión de algunos ULP cuando a un desglose completo cuando , donde es máquina-epsilon.exp(x) - 1
Para ampliar la diferencia entre
log
ylog1p
podría ayudar recordar el gráfico si el logaritmo:Si sus datos contienen ceros, entonces probablemente no quiera usarlos,x 0 ln(x) −∞ x 0 ln(x) ln(1e)=−1 ln(1e10)=−10
log
ya que no están definidos en cero. Y a medida que acerca a , el valor de acerca a . Entonces, si sus valores de están cerca de , entonces el valor de es potencialmente un número negativo grande. Por ejemplo y y así sucesivamente. Esto puede ser útil, pero también puede distorsionar sus datos hacia números negativos grandes, especialmente si su conjunto de datos también contiene números mucho más grandes que cero.Por otro lado, cuando acerca a , el valor de acerca a desde la dirección positiva. Por ejemplo y . Por lo tanto, produce solo valores positivos y elimina el "peligro" de los grandes números negativos. Esto generalmente asegura una distribución más homogénea cuando un conjunto de datos contiene números cercanos a cero.x 0 ln(x+1) 0 ln(1+1e)∼0.31 ln(1+1e10)∼0.000045
log1p
En resumen, si el conjunto de datos es mayor que , generalmente está bien. Pero, si el conjunto de datos tiene números entre y , generalmente es mejor.1 0 10 1
log
log1p
fuente