Chúng ta đều biết rằng
ngụ ý rằng cho , chúng ta có . Điều này có nghĩa là nếu chúng ta phải đánh giá theo dấu phẩy động , cho hủy bỏ thảm khốc có thể xảy ra.exp(x)=∑n=0∞xnn!=1+x+12x2+…
|x|≪1exp(x)≈1+xexp(x)−1|x|≪1
Điều này có thể dễ dàng chứng minh trong 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
Các giá trị chính xác là
exp(10−8)−1exp(10−22)−1=0.000000010000000050000000166666667083333334166666668…=0.000000000000000000000100000000000000000000005000000…
Nói chung, việc triển khai "chính xác" exp
và expm1
phải chính xác không quá 1ULP (tức là một đơn vị của vị trí cuối cùng). Tuy nhiên, vì việc đạt được độ chính xác này dẫn đến mã "chậm", đôi khi có triển khai nhanh, kém chính xác hơn. Ví dụ, trong CUDA chúng ta có expf
và expm1f
, f
viết tắt của từ nhanh. Theo hướng dẫn lập trình CUDA C, ứng dụng. D các expf
có lỗi của 2ULP.
Nếu bạn không quan tâm đến các lỗi theo thứ tự một vài ULPS, thông thường các cách triển khai khác nhau của hàm số mũ là tương đương, nhưng hãy cẩn thận rằng các lỗi có thể bị ẩn ở đâu đó ... (Bạn có nhớ lỗi Pentium FDIV không?)
Vì vậy, khá rõ ràng rằng expm1
nên được sử dụng để tính toán cho nhỏ . Sử dụng nó cho chung không có hại, vì dự kiến sẽ chính xác trong phạm vi đầy đủ của nó:exp(x)−1xxexpm1
>>> exp(200)-1 == exp(200) == expm1(200)
True
(Trong ví dụ ở trên thấp hơn 1ULP của , vì vậy cả ba biểu thức trả về chính xác cùng một số dấu phẩy động.)1exp(200)
Một cuộc thảo luận tương tự giữ cho các hàm nghịch đảo log
và log1p
vì cho .log(1+x)≈x|x|≪1
log1p
bạn đang đề cập (đặc biệt là cách nó được thực hiện, vì vậy chúng tôi không phải đoán).