ทำไมความละเอียดของจำนวนจุดลอยตัวจึงลดลงมากกว่าเดิม?


19

ฉาก OpenGL ของฉันมีวัตถุที่อยู่ในตำแหน่งที่ห่างไกลจากจุดกำเนิด เมื่อฉันดูวัตถุเหล่านี้และแพน / หมุน / ซูมกล้องรอบ ๆ วัตถุพวกเขาจะกระวนกระวายใจ นั่นคือจุดยอดที่ประกอบไปด้วยวัตถุดูเหมือนจะหมุนไปรอบ ๆ ตารางคะแนนสามมิติในจินตนาการ ฉันได้อ่านสิ่งนี้เป็นปัญหาที่พบบ่อยเนื่องจากจำนวนข้อมูลที่สามารถจัดเก็บโดยใช้ความแม่นยำจุดลอยตัว (ซึ่ง OpenGL และทุกอย่างอื่นใช้) ฉันไม่เข้าใจว่าทำไมสิ่งนี้ถึงเกิดขึ้น

เมื่อค้นหาวิธีแก้ปัญหาฉันเจอปัญหา 'ต้นกำเนิดแบบลอยตัว' ที่เรียบง่ายมากและดูเหมือนว่าจะใช้งานได้ ฉันแค่แปลงทุกอย่างเพื่อให้วัตถุของฉันอยู่ในตำแหน่งเดียวกัน แต่สิ่งที่กล้องของฉันดูอยู่ใกล้กับจุดกำเนิด ฉันพบคำอธิบายที่นี่: http://floatingorigin.com/แต่ฉันทำตามไม่ได้

ดังนั้น ... ใครบางคนสามารถอธิบายได้ว่าทำไมการวางฉากของฉันให้ห่างไกลมาก (พูดถึง 10 ล้านหน่วย) จากจุดกำเนิดทำให้เกิดพฤติกรรมที่ผิดปกติที่ฉันสังเกตเห็น? และทำไมการย้ายไปอยู่ใกล้กับจุดเริ่มต้นช่วยแก้ปัญหา


4
เพราะหากพวกเขาไม่ได้พวกเขาจะได้รับการแก้ไขหมายเลขจุด คำถามเกี่ยวกับเรื่องนี้
MSalters

1
จริง แต่เฉพาะเมื่อคุณเข้าใจความหมายของ 'จุดลอยตัว' อย่างแท้จริง
Kylotan

คำตอบ:


26

นี่คือทั้งหมดเนื่องจากวิธีการแสดงจุดลอยตัวในคอมพิวเตอร์

จำนวนเต็มถูกเก็บไว้ค่อนข้างตรงไปตรงมา; แต่ละหน่วยมี "หนึ่ง" อย่างแน่นอนนอกเหนือจาก "ก่อนหน้า" เช่นเดียวกับที่คุณคาดหวังกับตัวเลขที่นับได้

ด้วยตัวเลขจำนวนจุดลอยตัวนี่ไม่ใช่สิ่งที่แน่นอน แต่หลายบิตระบุว่า EXPONENT และส่วนที่เหลือบ่งบอกสิ่งที่เรียกว่าmantissaหรือส่วนที่เป็นเศษส่วนจากนั้นคูณด้วยส่วน exponent (โดยนัย 2 ^ exp) เพื่อให้ได้ผลลัพธ์สุดท้าย

ดูที่นี่สำหรับคำอธิบายภาพของบิต

มันเป็นอย่างแม่นยำเพราะเลขชี้กำลังนี้เป็นส่วนที่แท้จริงของบิตที่ความแม่นยำเริ่มต้นที่ WANE เมื่อตัวเลขเติบโตขึ้น

หากต้องการดูสิ่งนี้ในทางปฏิบัติลองทำจุดแทนค่า faux-floating โดยไม่ต้องเข้าไปใน nitty-gritty: ใช้เลขชี้กำลังขนาดเล็กเช่น 2 และทำบางส่วนเพื่อทดสอบ:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

... ฯลฯ

ตัวเลขเหล่านี้ไม่ได้ห่างกันมากในเลขชี้กำลัง 2 เท่านั้น แต่ตอนนี้ลองเลขชี้กำลัง 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

โอ้โหแตกต่างกันมากตอนนี้!

ตัวอย่างในขณะที่ไม่เฉพาะเจาะจงไปที่ VERY NEXT COUNTABLE มาก (นั่นจะเป็นส่วนที่เป็นเศษส่วนถัดไปขึ้นอยู่กับว่ามันมีจำนวนเท่าใด) จะต้องแสดงให้เห็นถึงการสูญเสียความแม่นยำเมื่อตัวเลขโตขึ้น หน่วย "จำนวนที่นับได้ถัดไป" ในหน่วยลอยมีขนาดเล็กมากพร้อมเลขชี้กำลังขนาดเล็กและใหญ่มากพร้อมเลขชี้กำลังขนาดใหญ่ในขณะที่จำนวนเต็มจะเป็น 1 เสมอ

เหตุผลที่วิธีการกำเนิดแบบลอยตัวทำงานได้เนื่องจากการปรับจำนวนจุดลอยตัวที่มีขนาดใหญ่เหล่านี้อาจลงไปเป็นเลขชี้กำลังขนาดเล็กเพื่อให้ "การนับถัดไป" (ความแม่นยำ) อาจเล็กและมีความสุขมาก


ตัวอย่างที่คุณให้เป็นตัวอย่างจริงๆขอบคุณ :)
Pris

3
ในเส้นทางที่ถูกต้อง แต่ฉันหวังว่าคุณจะได้ใช้ตัวอย่างที่ใกล้กับจุดลอยตัวที่ใช้งานได้จริง มันไม่ยกแมนทิสซาไปยังเลขชี้กำลัง มันคือ mantissa * 2 ^ เลขชี้กำลัง
นาธานรีด

3
คุณพูดถูกฉันรู้ ฉันไม่รู้ว่าฉันคิดอะไรอยู่ แก้ไขคำตอบของฉัน

1
@ScottW Nice แก้ไข! +1
นาธานรีด

17

เนื่องจากตัวเลขทศนิยมจะแสดงเป็นเศษส่วน + เครื่องหมาย + และคุณมีจำนวนบิตคงที่สำหรับส่วนเศษส่วน

http://en.wikipedia.org/wiki/Single_precision

เมื่อคุณได้รับตัวเลขที่มากขึ้นเรื่อย ๆ คุณจะไม่มีบิตที่จะแสดงส่วนที่เล็กกว่า


8

คลาสสิกในสนามจะต้องถูกนำขึ้น: ทุกสิ่งที่นักวิทยาศาสตร์คอมพิวเตอร์ควรรู้เกี่ยวกับตัวเลขทศนิยม

แต่สิ่งสำคัญของมันคือการทำอย่างไรกับหมายเลขทศนิยมที่มีความแม่นยำ (สองเท่า) เป็นเพียงเลขฐานสองแบบ 32 บิต (64- บิต) ที่มี 1 บิตที่เป็นสัญลักษณ์แทนเลขชี้กำลัง 8 บิต (11 บิต) ของฐาน 2 และนัยสำคัญ 23 บิต (52- บิต) และ (วงเล็บคือค่าสำหรับคู่)

นั่นหมายความว่าจำนวนบวกที่เล็กที่สุดที่คุณสามารถเป็นตัวแทนในความแม่นยำเดียว 0.0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1.40 x 10 -45

จำนวนบวกถัดไปคือสองเท่า: 0.0000000000000000000010 x 2 -127 = 2 -148 ~ 2.80 x 10 -45แล้วตัวเลขถัดไปคือผลรวมของสองก่อนหน้านี้ 0.00000000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4.2 - 45 .

สิ่งนี้ยังคงเพิ่มขึ้นตามความแตกต่างคงที่เดิม ๆ จนกระทั่ง: 0.1111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1.17549435 x 10 -38 - 0.00000014 x 10 -38 = 1.17549421 x 10 -38

ตอนนี้คุณได้มาถึงหมายเลขปกติ (โดยที่ตัวเลขตัวแรกในซิกนิแคนด์คือ 1) โดยเฉพาะ: 1.00000000000000000000000000 x 2 -126 = 2 -126 = 1.17549435 x 10 -38และตัวเลขถัดไปคือ 1.00000000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 x 1.00000023


2

สาเหตุที่ตัวเลขจุดลอยตัวมีความแม่นยำน้อยกว่ามากจากจุดเริ่มต้นเนื่องจากตัวเลขจุดลอยตัวควรจะสามารถแสดงจำนวนมากได้ วิธีการนี้จะทำให้ยืมคำว่า "จุดลอย" มันแยกค่าที่เป็นไปได้ที่มันสามารถรับได้ (ซึ่งถูกกำหนดโดยความยาวบิตของมัน) เพื่อให้มีจำนวนใกล้เคียงกันสำหรับแต่ละเลขชี้กำลัง: สำหรับทุ่น 32- บิต 23 ของบิตกำหนด mantissa หรือซิกนิฟิแคนด์ ดังนั้นจะสามารถใช้ค่าของ 2 ^ 23 ค่าที่แตกต่างกันในแต่ละช่วงเลขชี้กำลัง หนึ่งในช่วงเลขชี้กำลังเหล่านี้คือ 1-2 [2 ^ 0 ถึง 2 ^ 1] ดังนั้นการแยกช่วง 1 ถึง 2 เป็น 2 ^ 23 ค่าที่แตกต่างกันช่วยให้มีความแม่นยำมาก

แต่การแยกช่วง [2 ^ 10 ถึง 2 ^ 11] เป็น 2 ^ 23 ค่าที่แตกต่างหมายถึงช่องว่างระหว่างแต่ละค่านั้นใหญ่กว่ามาก หากไม่เป็นเช่นนั้น 23 บิตจะไม่เพียงพอ สิ่งทั้งหมดคือการประนีประนอม: คุณต้องมีจำนวนบิตไม่ จำกัด เพื่อแสดงจำนวนจริงใด ๆ หากแอปพลิเคชันของคุณทำงานในลักษณะที่ช่วยให้คุณหลีกเลี่ยงความแม่นยำที่ต่ำลงสำหรับค่าที่มากขึ้นและคุณจะได้รับประโยชน์จากความสามารถแสดงค่าขนาดใหญ่ได้จริงคุณต้องใช้การแสดงจุดลอยตัว


เพียงแค่จดบันทึกที่นี่เมื่อตรวจสอบเพิ่มเติม 7 ปีต่อมา ... ตัวเลขของฉันในตัวอย่างของฉันไม่ได้เลือกที่ดีโดยเฉพาะ แต่คะแนนโดยรวมนั้นถูกต้อง
สตีเวนลู

1

อาจเป็นเรื่องยากที่จะเสนอตัวอย่างเฉพาะเจาะจงเกี่ยวกับวิธีการทำงานของความแม่นยำจุดลอยตัว เพื่อเสริมคำตอบอื่น ๆ ที่นี่เป็นหนึ่งใน สมมติว่าเรามีเลขทศนิยมแบบทศนิยมโดยมีสามหลักของแมนทิสซาและเลขชี้กำลังหนึ่งหลัก:

แมนทิสสา× 10 เลขชี้กำลัง

เมื่อเลขชี้กำลังเป็น 0 ทุกจำนวนเต็มในช่วง 0–999 สามารถแสดงได้อย่างแม่นยำ เมื่อเป็น 1 คุณจะคูณทุกองค์ประกอบของช่วงนั้นเป็น 10 ดังนั้นคุณจะได้ช่วง 0–9990; แต่ตอนนี้สามารถแสดงได้เพียง 10 รายการเท่านั้นเนื่องจากคุณยังมีความแม่นยำเพียงสามหลัก เมื่อเลขชี้กำลังเป็นที่สูงสุด 9 ความแตกต่างระหว่างคู่ของจำนวนเต็มซึ่งแสดงแต่ละคนเป็นหนึ่งพันล้าน คุณกำลังทำการซื้อขายอย่างแม่นยำสำหรับช่วง

มันทำงานในลักษณะเดียวกันกับไบนารีจำนวนจุดลอยตัว: เมื่อใดก็ตามที่ตัวแทนขึ้นไปโดยหนึ่งในช่วงคู่ผสมแต่จำนวนของค่าซึ่งแสดงอยู่ในช่วงที่มีการลดลงครึ่งหนึ่ง สิ่งนี้ใช้กับตัวเลขที่เป็นเศษส่วนเช่นกันซึ่งแน่นอนว่าเป็นแหล่งที่มาของปัญหาของคุณ


0

โดยทั่วไปความละเอียดจะแย่ลงเนื่องจากความละเอียดคูณด้วยค่าเลขชี้กำลัง (2 ** ส่วนที่เป็นเลขชี้กำลัง)

ในการรับทราบความคิดเห็นของโจชัว: ข้างต้นเป็นเพียงการใส่คำตอบลงในคำสั่งสั้น ๆ แน่นอนว่าฉันพยายามระบุบนhttp://floatingorigin.com/นี่เป็นเพียงการเริ่มต้นไปสู่โซลูชันโดยรวมและโปรแกรมของคุณอาจมีปัญหาจากหลาย ๆ จุด: ในท่อที่แม่นยำหรือส่วนอื่น ๆ ของรหัส .


สิ่งนี้จะไม่เพิ่มสิ่งใด ๆ ที่ไม่ได้มีอยู่ในคำตอบอื่น ๆ

จริง: ฉันรู้ว่าฉันสามารถอธิบายคำตอบในบรรทัดเดียวและคิดว่าบางคนอาจพบว่าคำตอบสั้น ๆ มีประโยชน์
Chris Thorne

-1

OpenGL บัฟเฟอร์ความลึกไม่เป็นเชิงเส้น ยิ่งคุณไปไกลเท่าใดความละเอียดก็ยิ่งแย่ลงเท่านั้น ผมขอแนะนำให้อ่านนี้ สิ่งที่นำมาจากที่นั่น (12.070):

โดยสรุปการแบ่งมุมมองตามธรรมชาติทำให้ความแม่นยำ Z ใกล้กับด้านหน้าของปริมาณการดูมากกว่าใกล้ด้านหลัง

และอีกอันหนึ่ง (12.040):

คุณอาจกำหนดค่า zNear และ zFar clipping planes ในวิธีที่จำกัดความแม่นยำของบัฟเฟอร์ความลึกอย่างรุนแรง โดยทั่วไปเกิดจากค่าระนาบการตัด zNear ที่ใกล้เคียงกับ 0.0 มากเกินไป เมื่อระนาบการตัด zNear ถูกตั้งค่าไว้ใกล้กับ 0.0 มากขึ้นความแม่นยำที่มีประสิทธิภาพของบัฟเฟอร์ความลึกจะลดลงอย่างมาก การขยับระนาบตัด zFar ให้ห่างจากตาจะมีผลกระทบด้านลบต่อความแม่นยำของบัฟเฟอร์ความลึกเสมอ

ดังนั้นคุณควรเลื่อนระนาบที่อยู่ใกล้ที่สุดเท่าที่จะทำได้และระนาบไกลของคุณให้อยู่ใกล้ที่สุดเท่าที่จะทำได้


-1: คำถามเกี่ยวกับความแม่นยำจุดลอยตัวไม่ใช่ปัญหาความแม่นยำด้วยการแสดงบัฟเฟอร์ความลึกแบบไม่เชิงเส้น
นาธานรีด

เป็นไปได้ว่าสิ่งที่ฉันเห็นเป็นเพราะปัญหาบัฟเฟอร์ลึก ฉันใช้ lib ที่ด้านบนของ OpenGL เพื่อดูฉากของฉันและฉันตั้งสมมติฐานว่ามันตั้งค่ากล้องมุมมองและระนาบการตัดที่ใกล้และไกลเพื่อพิจารณาขนาดและตำแหน่งของรูปทรงเรขาคณิต (เนื่องจากผู้ดู เครื่องมือดูเหมือนจะตั้งค่ามุมมองที่เหมาะสมที่สุดให้กับเนื้อหาของฉากโดยอัตโนมัติ) แต่ฉันเดาว่านี่อาจไม่เป็นอย่างนั้น - ฉันจะลองเล่นกับเครื่องบินรูปวาดที่ออกจากตำแหน่งเดิมเหมือนเดิมและดูว่าเกิดอะไรขึ้น
ริส

2 นาธานรีด: ผู้เขียนเขียนว่าเขามีฉาก OpenGL ดังนั้นฉันคิดว่ามันอาจเป็นปัญหานี้เช่นกัน
zacharmarz

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