Kita semua tahu bahwa
exp(x)=∑n=0∞xnn!=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(10−8)−1exp(10−22)−1=0.000000010000000050000000166666667083333334166666668…=0.000000000000000000000100000000000000000000005000000…
Secara umum implementasi "akurat" dari exp
dan expm1
harus 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 expf
dan expm1f
, di mana f
singkatan dari fast. Menurut panduan pemrograman CUDA C, aplikasi. D yang expf
memiliki 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 expm1
harus digunakan untuk menghitung exp(x)−1 untuk x kecil . Menggunakannya untuk x umum tidak berbahaya, karena expm1
diharapkan 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 log
dan log1p
karena log(1+x)≈x untuk |x|≪1 .
log1p
Anda maksud (terutama bagaimana penerapannya, jadi kami tidak perlu menebak).