นี่คือสิ่งที่เกิดขึ้นในไบนารี อย่างที่เราทราบค่าจุดลอยตัวบางค่าไม่สามารถแสดงได้อย่างแน่นอนในรูปแบบไบนารี่ ตัวเลข 3 ตัวนี้เป็นเพียงตัวอย่างของข้อเท็จจริงนั้น
ด้วยโปรแกรมนี้ฉันจะแสดงตัวเลขฐานสิบหกของแต่ละตัวเลขและผลลัพธ์ของการเติมแต่ละครั้ง
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(x);
printValueAndInHex(y);
printValueAndInHex(z);
printValueAndInHex(s);
System.out.println("--------");
double t1 = x + y;
printValueAndInHex(t1);
t1 = t1 + z;
printValueAndInHex(t1);
System.out.println("--------");
double t2 = x + z;
printValueAndInHex(t2);
t2 = t2 + y;
printValueAndInHex(t2);
}
private static void printValueAndInHex(double d)
{
System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
}
}
printValueAndInHex
วิธีการเป็นเพียงผู้ช่วยที่ฐานสิบหกเครื่องพิมพ์
ผลลัพธ์มีดังนี้:
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
4 หมายเลขแรกx
, y
, z
และs
's แสดงเลขฐานสิบหก ในการแทนค่าทศนิยม IEEE บิต 2-12 แสดงเลขชี้กำลังเลขฐานสองนั่นคือมาตราส่วนของจำนวน (บิตแรกคือบิตเครื่องหมายและบิตที่เหลือสำหรับmantissa ) เลขชี้กำลังเป็นตัวแทนจริง ๆ แล้วเป็นเลขฐานสองลบด้วย 1023
เลขชี้กำลังสำหรับ 4 หมายเลขแรกถูกแยกออกมา:
sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
ชุดเพิ่มเติม
ตัวเลขที่สอง ( y
) มีขนาดเล็กกว่า เมื่อเพิ่มตัวเลขสองตัวนี้เพื่อรับx + y
2 บิตสุดท้ายของตัวเลขที่สอง ( 01
) จะถูกเลื่อนออกจากช่วงและไม่ต้องคำนวณลงในการคำนวณ
การเพิ่มครั้งที่สองจะเพิ่มx + y
และz
เพิ่มตัวเลขสองตัวที่มีขนาดเท่ากัน
ชุดที่สองของการเพิ่ม
ที่นี่x + z
เกิดขึ้นก่อน มีขนาดเท่ากัน แต่ให้จำนวนที่สูงกว่าในระดับ:
404 => 0|100 0000 0100| => 1028 - 1023 = 5
การเพิ่มครั้งที่สองจะเพิ่มx + z
และy
และในตอนนี้3บิตจะถูกดรอปจากy
เพื่อเพิ่มหมายเลข ( 101
) ที่นี่จะต้องมีการปัดขึ้นเนื่องจากผลลัพธ์คือเลขทศนิยมถัดไปขึ้น: 4047866666666666
สำหรับการเพิ่มชุดแรกกับ4047866666666667
การเติมชุดที่สอง ข้อผิดพลาดนั้นสำคัญพอที่จะแสดงในผลรวมของการพิมพ์
โดยสรุปให้ใช้ความระมัดระวังในการดำเนินการทางคณิตศาสตร์กับหมายเลข IEEE การรับรองบางอย่างนั้นไม่แน่นอนและพวกมันจะไม่แน่นอนมากขึ้นเมื่อตาชั่งแตกต่างกัน เพิ่มและลบตัวเลขของสเกลที่คล้ายกันถ้าคุณทำได้
(2.0^53 + 1) - 1 == 2.0^53 - 1 != 2^53 == 2^53 + (1 - 1)
) ดังนั้นใช่: ระวังเมื่อเลือกลำดับของผลรวมและการดำเนินการอื่น ๆ บางภาษามีการติดตั้งภายในเพื่อแสดงผลรวมของ "ความแม่นยำสูง" (เช่น pythonmath.fsum
) ดังนั้นคุณอาจลองใช้ฟังก์ชั่นเหล่านี้แทนอัลกอริธึมแบบไร้เดียงสา