ความแม่นยำ 'ลอย' เทียบกับ 'สองเท่า'


155

รหัส

float x  = 3.141592653589793238;
double z = 3.141592653589793238;
printf("x=%f\n", x);
printf("z=%f\n", z);
printf("x=%20.18f\n", x);
printf("z=%20.18f\n", z);

จะให้ผลลัพธ์

x=3.141593
z=3.141593
x=3.141592741012573242
z=3.141592653589793116

โดยที่บรรทัดที่สามของการส่งออก741012573242เป็นขยะและในบรรทัดที่สี่116คือขยะ คู่ผสมมีตัวเลขสำคัญ 16 ตัวเสมอหรือไม่ในขณะที่ลอยตัวมีตัวเลขสำคัญ 7 ตัวอยู่เสมอ? ทำไมคู่ไม่ได้มี 14 ตัวเลขที่สำคัญ?

คำตอบ:


146

ตัวเลขทศนิยมใน C ใช้การเข้ารหัสIEEE 754

การเข้ารหัสชนิดนี้ใช้เครื่องหมายซิกนิฟิแคนด์และเลขชี้กำลัง

เนื่องจากการเข้ารหัสนี้ตัวเลขจำนวนมากจะมีการเปลี่ยนแปลงเล็กน้อยเพื่อให้สามารถจัดเก็บได้

นอกจากนี้จำนวนของตัวเลขที่สำคัญสามารถเปลี่ยนแปลงได้เล็กน้อยเนื่องจากมันเป็นตัวแทนไบนารีไม่ใช่ทศนิยมหนึ่ง

ความแม่นยำเดี่ยว (ลอย) ให้คุณ 23 บิตของซิกนิแคนด์, 8 บิตเลขชี้กำลังและ 1 เครื่องหมายบิต

ความแม่นยำสองเท่า (สองเท่า) ช่วยให้คุณมีซิกนิฟิแคนท์ 52 บิตเอ็กซ์โปเนนท์ 11 บิตและ 1 เครื่องหมายบิต


4
C99 ทำก่อนหน้านี้มันขึ้นอยู่กับคอมไพเลอร์
Alan Geleynse

21
-1 ข้อความนี้เป็นเท็จโจ๋งครึ่ม: "เนื่องจากการเข้ารหัสนี้คุณไม่สามารถรับประกันได้ว่าคุณจะไม่เปลี่ยนแปลงค่าของคุณ"
.. GitHub หยุดช่วยน้ำแข็ง

16
@Alan: C99 ไม่ต้องการจุดลอยตัว IEEE; มันเพิ่งแนะนำให้มัน
.. GitHub หยุดช่วยน้ำแข็ง

4
@Alan: R .. ถูกต้อง; ภาคผนวก F (ซึ่งระบุ IEEE-754 ผูก) เป็นบรรทัดฐาน __STDC_IEC_559__แต่เฉพาะในผลกระทบหากมีการดำเนินการกำหนด การใช้งานที่ไม่ได้กำหนดว่าแมโครนั้นฟรีไม่สอดคล้องกับ IEEE-754
Stephen Canon

12
@Alan: ภายใต้มาตรฐาน IEEE 754 ก็รับประกันได้อย่างง่ายดายว่ามีการเปลี่ยนแปลงในค่าไม่มี0.5, 0.046875หรือ0.376739501953125เมื่อเทียบกับการแสดงทศนิยมของพวกเขา (นี่คือเหตุผลทั้งหมดเกี่ยวกับ diadic ที่มีตัวเศษใน mantissa และลอการิทึมฐาน 2 ของการปรับตัวส่วนใน exponent)
R .. GitHub STOP ช่วย ICE

42

คู่ผสมมีตัวเลขสำคัญ 16 ตัวเสมอหรือไม่ในขณะที่ลอยตัวมีตัวเลขสำคัญ 7 ตัวอยู่เสมอ?

ไม่ Doubles จะมี 53 บิตที่สำคัญและลอยอยู่เสมอมี 24 บิตที่สำคัญ(ยกเว้น denormals, infinities และ NaN แต่สิ่งเหล่านี้เป็นหัวข้อสำหรับคำถามที่แตกต่างกัน) นี่คือรูปแบบไบนารีและคุณสามารถพูดได้อย่างชัดเจนเกี่ยวกับความแม่นยำของการเป็นตัวแทนในรูปของเลขฐานสอง (บิต)

นี่คล้ายกับคำถามที่ว่าสามารถเก็บตัวเลขจำนวนเต็มเป็นจำนวนเต็มแบบไบนารีได้อย่างไร: จำนวนเต็มแบบ 32 บิตที่ไม่ได้ลงนามสามารถจัดเก็บจำนวนเต็มได้สูงสุด 32 บิตซึ่งไม่ตรงกับตัวเลขทศนิยมจำนวนใด ๆ อย่างแม่นยำ: จำนวนเต็มทั้งหมดถึง สามารถเก็บทศนิยม 9 หลัก แต่สามารถเก็บตัวเลข 10 หลักได้เช่นกัน

ทำไมคู่ไม่ได้มี 14 ตัวเลขที่สำคัญ?

การเข้ารหัสของ double ใช้ 64 บิต (1 บิตสำหรับเครื่องหมาย, 11 บิตสำหรับเลขชี้กำลัง, 52 บิตที่มีนัยสำคัญชัดเจนและหนึ่งบิตโดยนัย) ซึ่งเป็นสองเท่าของจำนวนบิตที่ใช้เพื่อเป็นตัวแทนของ float (32 บิต)


15

float: ซิกนิฟิแคนด์ 23 บิต, exponent 8 บิตและ 1 sign bit

double: 52 บิตของซิกนิฟิแคนด์, 11 บิตของเลขชี้กำลังและ 1 เครื่องหมายบิต


11

มันมักจะขึ้นอยู่กับตัวเลขที่สำคัญของทั้งเลขชี้กำลังและซิกนิแคนด์ในฐาน 2 ไม่ใช่ฐาน 10 จากสิ่งที่ฉันสามารถบอกได้ในมาตรฐาน C99 อย่างไรก็ตามไม่มีความแม่นยำที่ระบุสำหรับโฟลตและดับเบิล (นอกเหนือจากข้อเท็จจริงที่ 1 และ1 + 1E-5/ 1 + 1E-7สามารถแยกแยะความแตกต่าง [ floatและdoublerepsectively]) อย่างไรก็ตามจำนวนของตัวเลขที่มีนัยสำคัญนั้นถูกปล่อยไว้ให้ผู้ดำเนินการ (เช่นเดียวกับฐานที่พวกเขาใช้ภายในดังนั้นในคำอื่น ๆ การดำเนินการสามารถตัดสินใจที่จะทำให้มันขึ้นอยู่กับความแม่นยำ 18 หลักในฐาน 3) [1]

หากคุณจำเป็นต้องรู้ค่าเหล่านี้ค่าคงที่FLT_RADIXและFLT_MANT_DIG(และDBL_MANT_DIG/ LDBL_MANT_DIG) จะถูกกำหนดใน float.h

เหตุผลที่เรียกว่า a doubleเป็นเพราะจำนวนไบต์ที่ใช้ในการจัดเก็บนั้นเป็นสองเท่าของจำนวนลอย (แต่รวมถึงทั้งเลขชี้กำลังและซิกนิแคนด์) มาตรฐาน IEEE 754 (ใช้โดยคอมไพเลอร์ส่วนใหญ่) จัดสรรบิตสำหรับซิกนิฟิแคนด์และเลขชี้กำลัง (23 ถึง 9 สำหรับfloatเทียบกับ 52 ถึง 12 สำหรับdouble) ซึ่งเป็นเหตุผลว่าทำไมความแม่นยำจึงเพิ่มเป็นสองเท่า

1: ส่วน 5.2.4.2.2 ( http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf )


สะกดผิด? C89 ต้องมี epsilon ของที่มากที่สุด1E-9สำหรับการไม่ได้double 1E-7
Rufflewind

10

ทุ่นมีความแม่นยำ 23 บิตและสองเท่ามี 52


รายละเอียด: binary64มีความสำคัญ 53 บิต (เก็บไว้ 52 อย่างชัดเจน) binary32มี 24 บิต (23 เก็บไว้อย่างชัดเจน)
chux - Reinstate Monica

4

มันไม่ได้มีความแม่นยำเป็นสองเท่าเพราะวิธีการทำงานของIEEE 754และเนื่องจากไบนารีไม่ได้แปลเป็นทศนิยมได้ดี ลองดูที่มาตรฐานหากคุณสนใจ


4

float ย่อมาจากจำนวนจุดลอยใน C, ชนิดข้อมูลลอยถูกนำมาใช้ในกรณีที่มีความแม่นยำของจำนวนหลักของทั้งหมดคือ 7. สำหรับเช่น: - เลขทศนิยม 12.3546987 ไม่สามารถเก็บในรูปแบบลอยได้เนื่องจากมีจำนวน 9 หลักผลลัพธ์จะแสดงเป็น 12.354699 นั่นคือตัวเลข 7 ตัวแรกจะแสดงตามที่ป้อนในอินพุตและตัวเลข 8 จะถูกปัดเศษชนิดทศนิยมสามารถแทนค่าได้ ตั้งแต่ประมาณ 1.5 x 10 ^ (- 45) ถึง 3.4 x 10 ^ (38). ในแง่ของการจัดสรรหน่วยความจำ, float เป็นชนิดข้อมูลจุดความแม่นยำเดี่ยว 32 บิต

ซึ่งมีความแม่นยำ 15 ถึง 16 หลักช่วงของ double คือ 5.0 × 10 ^ (- 345) ถึง 1.7 × 10 ^ (308) ในแง่ของการจัดสรรไบต์ double เป็นข้อมูลทศนิยม 64 บิต ชนิด

ปัญหาเกิดขึ้นในการใช้งาน float หรือ double ไม่มีผลต่อ printf แต่ในกรณีของ scanf จะใช้ชนิดข้อมูลที่เหมาะสมขึ้นอยู่กับจำนวนรวม ของตัวเลขในจำนวนลอยตัว ที่จะอ่านจากอินพุต

ดังนั้นจึงเป็นที่ต้องการมากกว่าลอยเพื่อความถูกต้องของข้อมูลที่สูงขึ้น

หวังว่านี่จะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.