Kapan sebaiknya log1p dan expm1 digunakan?

30

Saya punya pertanyaan sederhana yang benar-benar sulit bagi Google (di samping kanonik Apa Yang Harus Diketahui Setiap Ilmuwan Komputer tentang kertas Aritmatika Titik Apung ).

Kapan seharusnya fungsi seperti log1patau expm1digunakan, bukan logdan exp? Kapan mereka tidak digunakan? Bagaimana perbedaan implementasi dari fungsi-fungsi tersebut berbeda dalam hal penggunaannya?

Tim
sumber
2
Selamat datang di Scicomp.SE! Itu pertanyaan yang sangat masuk akal, tetapi akan lebih mudah dijawab jika Anda menjelaskan sedikit yang log1p Anda maksud (terutama bagaimana penerapannya, jadi kami tidak perlu menebak).
Christian Clason
4
Untuk argumen bernilai riil, log1p dan expm1 harus digunakan ketika(x)(x)x kecil, misalnya, ketika dalam akurasi floating point. Lihat, misalnya, docs.scipy.org/doc/numpy/reference/generated/numpy.expm1.html dan docs.scipy.org/doc/numpy/reference/generated/numpy.log1p.html . 1+x=1
GoHokies
@ChristianClason terima kasih, saya merujuk sebagian besar ke C ++ std atau R, tetapi ketika Anda bertanya saya mulai berpikir bahwa belajar tentang perbedaan dalam implementasi juga akan sangat menarik.
Tim
Berikut ini contohnya: scicomp.stackexchange.com/questions/8371/…
Juan M. Bello-Rivas
1
@ user2186862 "ketika kecil" sudah benar, tetapi tidak hanya "ketika 1 + x = 1 dalam akurasi floating point" (yang terjadi pada x 10 - 16 dalam aritmatika presisi ganda biasa). Halaman dokumentasi yang Anda tautkan menunjukkan bahwa mereka sudah berguna untuk x 10 - 10 , misalnya. x1+x=1x1016x1010
Federico Poloni

Jawaban:

25

Kita semua tahu bahwa

exp(x)=n=0xnn!=1+x+12x2+
menyiratkan bahwa untuk|x|1, kami memilikiexp(x)1+x. Ini berarti bahwa jika kita harus mengevaluasi dalam floating pointexp(x)1, untuk|x|1pembatalan katastropik dapat terjadi.

Ini dapat dengan mudah ditunjukkan dengan 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

Nilai yang tepat adalah

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

Secara umum implementasi "akurat" dari expdan expm1harus benar tidak lebih dari 1ULP (yaitu satu unit tempat terakhir). Namun, karena mencapai akurasi ini menghasilkan kode "lambat", terkadang implementasi yang cepat dan kurang akurat tersedia. Misalnya dalam CUDA yang kita miliki expfdan expm1f, di mana fsingkatan dari fast. Menurut panduan pemrograman CUDA C, aplikasi. D yang expfmemiliki kesalahan dari 2ULP.

Jika Anda tidak peduli tentang kesalahan dalam urutan beberapa ULPS, biasanya implementasi yang berbeda dari fungsi eksponensial adalah setara, tetapi berhati-hatilah bahwa bug mungkin disembunyikan di suatu tempat ... (Ingat bug Pentium FDIV ?)

Jadi cukup jelas bahwa expm1harus digunakan untuk menghitung exp(x)1 untuk x kecil . Menggunakannya untuk x umum tidak berbahaya, karena expm1diharapkan akurat pada kisaran lengkapnya:

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

(Dalam contoh di atas 1 jauh di bawah 1ULP exp(200) , jadi ketiga ekspresi mengembalikan angka floating point yang persis sama.)

Diskusi serupa berlaku untuk fungsi terbalik logdan log1pkarena log(1+x)x untuk |x|1 .

Stefano M
sumber
1
Jawaban ini sudah terkandung dalam komentar untuk pertanyaan OP. Namun saya merasa berguna untuk memberikan akun yang lebih panjang (meskipun mendasar) hanya untuk kejelasan, dengan harapan itu akan bermanfaat bagi beberapa pembaca.
Stefano M
OK, tapi kemudian orang bisa menyimpulkan "jadi saya selalu bisa menggunakan expm1 daripada exp" ...
Tim
1
@ Tim kesimpulan Anda salah: Anda dapat selalu menggunakan expm1(x)bukan exp(x)-1. Tentu saja exp(x) == exp(x) - 1tidak berlaku secara umum.
Stefano M
x1
1
expm1(x)0x1exp(x) - 1x1x<ϵϵ
1

Untuk memperluas perbedaan antara logdan log1pmungkin membantu untuk mengingat grafik jika logaritma:

Logaritma

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

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

1log01log1p

sfmiller940
sumber