Коли слід використовувати log1p та expm1?


30

У мене є просте запитання, яке Google дуже важко (окрім канонічного, що повинен знати кожен комп'ютерний арифметичний папір з плаваючою комою ).

Коли такі функції, як log1pабо expm1повинні використовуватися замість logі exp? Коли їх не слід використовувати? Як різняться реалізація цих функцій за рівнем їх використання?


2
Ласкаво просимо до Scicomp.SE! Це дуже розумне питання, але було б простіше відповісти, якби ви пояснили трохи, на що log1p ви посилаєтесь (особливо, як це реалізовано, тому нам не доведеться здогадуватися).
Крістіан Класон

4
Для аргументів реальної цінності log1p (x) та expm1 (x) слід використовувати, коли x малий, наприклад, коли 1+x=1 з точністю з плаваючою комою. Див., Наприклад, docs.scipy.org/doc/numpy/reference/generated/numpy.expm1.html та docs.scipy.org/doc/numpy/reference/generated/numpy.log1p.html .
GoHokies

@ChristianClason дякую, я здебільшого маю на увазі C ++ std або R, але, як ви запитуєте, я починаю думати, що дізнаватися про відмінності в реалізації також було б дуже цікаво.
Тім


1
@ User2186862 «коли мала» є правильним, але не тільки «коли 1 + х = 1 в точності з плаваючою точкою» (який відбувається за е 10 - 16 в звичайній подвійної точності арифметики). Сторінки документації, на які ви пов’язані, показують, що вони корисні, наприклад, для x 10 - 10 . x1+x=1x1016x1010
Федеріко Полоні

Відповіді:


25

Всі ми знаємо, що

exp(x)=n=0xnn!=1+x+12x2+
означає, що для|x|1, маємоexp(x)1+x. Це означає, що якщо нам доведеться оцінювати в плаваючій точціexp(x)1, для|x|1може статися катастрофічне скасування.

Це можна легко продемонструвати на 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

Точні значення є

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

Загалом "точна" реалізація expта expm1повинна бути правильною не більше 1ULP (тобто однієї одиниці останнього місця). Однак, оскільки досягнення цієї точності призводить до "повільного" коду, іноді доступна швидка, менш точна реалізація. Наприклад, у CUDA у нас є expfі expm1f, де fозначає швидкість. Відповідно до посібника з програмування CUDA C, додаток. Дexpf має похибку 2ULP.

Якщо вас не хвилюють помилки в порядку декількох ULPS, зазвичай різні реалізації експоненціальної функції рівноцінні, але будьте обережні, що помилки можуть бути десь заховані ... (Пам'ятаєте помилку Pentium FDIV ?)

Тож цілком зрозуміло, що expm1слід використовувати для обчислення exp(x)1 для малих x . Використання його для загального x не є шкідливим, оскільки, expm1як очікується, буде точним за весь його діапазон:

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

(У наведеному вище прикладі 1 набагато нижче 1ULP exp(200) , тому всі три вирази повертають точно таке ж число з плаваючою точкою.)

Аналогічне обговорення стосується і зворотних функцій, logі log1pоскільки log(1+x)x для |x|1 .


1
Ця відповідь містилася вже у коментарях до питання про ОП. Однак мені здалося корисним дати більш тривалий (хоча і базовий) рахунок лише для ясності, сподіваючись, що він буде корисним для деяких читачів.
Стефано М

Гаразд, але тоді можна просто зробити висновок "так що я завжди можу використовувати expm1 замість досвіду" ...
Тім,

1
@tim ваш висновок неправильний: ви завжди можете використовувати expm1(x)замість цьогоexp(x)-1 . Звичайно, exp(x) == exp(x) - 1взагалі не тримається.
Стефано М

Гаразд, це зрозуміло. А чи є чіткі критерії скорочення для ? x1
Тим

1
@Tim немає чіткого порогу скорочення, і відповідь залежить від точності реалізації плаваючої точки (і проблеми, яка вирішується). Хоча expm1(x)має бути точним до 1ULP протягом усього діапазону , прогресивно втрачає точність від декількох ULP, коли x 1, до повного розбиття, коли x < ϵ , де ϵ - машинно-епсилон. 0x1exp(x) - 1x1x<ϵϵ
Стефано М

1

Щоб розширити різницю між, logі log1pце може допомогти згадати графік, якщо логарифм:

Logarithm

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

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

1log01log1p

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.