เราทุกคนรู้ว่า
exp(x)=∑n=0∞xnn!=1+x+12x2+…
บอกเป็นนัยสำหรับ|x|≪1เรามีexp(x)≈1+xx ซึ่งหมายความว่าหากเราต้องประเมินค่าใน floating pointexp(x)−1, สำหรับ|x|≪1ภัยพิบัติการยกเลิกสามารถเกิดขึ้นได้
สิ่งนี้สามารถแสดงให้เห็นได้อย่างง่ายดายในไพ ธ อน:
>>> 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(10−8)−1exp(10−22)−1=0.000000010000000050000000166666667083333334166666668…=0.000000000000000000000100000000000000000000005000000…
โดยทั่วไปการดำเนินการ "ถูกต้อง" ของexp
และexpm1
ควรถูกต้องไม่เกิน 1ULP (เช่นหนึ่งหน่วยของสถานที่สุดท้าย) อย่างไรก็ตามเนื่องจากการบรรลุความแม่นยำนี้ส่งผลให้มีรหัส "ช้า" บางครั้งมีการใช้งานที่รวดเร็วและแม่นยำน้อยกว่า ตัวอย่างเช่นใน CUDA เรามีexpf
และexpm1f
ที่f
ย่อมาจากอย่างรวดเร็ว ตามคู่มือการเขียนโปรแกรม CUDA C แอป D the expf
มีข้อผิดพลาดของ 2ULP
หากคุณไม่สนใจเกี่ยวกับข้อผิดพลาดตามลำดับของ ULPS เพียงไม่กี่ครั้งการใช้งานฟังก์ชันเลขชี้กำลังต่างกันจะเท่ากัน แต่ระวังว่าข้อบกพร่องอาจถูกซ่อนอยู่ที่ไหนสักแห่ง ... (จำข้อผิดพลาด Pentium FDIV ได้ไหม)
ดังนั้นจึงสวยชัดเจนว่าexpm1
ควรจะใช้ในการคำนวณexp(x)−1สำหรับธุรกิจขนาดเล็กxxการใช้งานกับxทั่วไปนั้นไม่เป็นอันตรายเนื่องจากexpm1
คาดว่าจะแม่นยำตลอดช่วงเต็มรูปแบบ:
>>> exp(200)-1 == exp(200) == expm1(200)
True
(ในตัวอย่างข้างต้น1มีค่าต่ำกว่า 1ULP ของexp(200)ดังนั้นทั้งสามนิพจน์จะคืนค่าหมายเลขทศนิยมเหมือนกันทุกประการ)
การสนทนาที่คล้ายกันมีไว้สำหรับฟังก์ชันผกผันlog
และlog1p
ตั้งแต่log(1+x)≈xสำหรับ|x|≪1 1
log1p
คุณอ้างถึง (โดยเฉพาะอย่างยิ่งวิธีการนำไปปฏิบัติ