ทำไมคอมพิวเตอร์ไม่เก็บตัวเลขทศนิยมเป็นจำนวนเต็มตัวที่สอง


24

คอมพิวเตอร์มีปัญหาในการจัดเก็บตัวเลขเศษส่วนที่ตัวส่วนเป็นส่วนอื่นที่ไม่ใช่ 2 ^ x นี่เป็นเพราะตัวเลขตัวแรกหลังจากจุดทศนิยมมีค่า 1/2, 1/4 ที่สอง (หรือ 1 / (2 ^ 1) และ 1 / (2 ^ 2)) เป็นต้น

เหตุใดจึงต้องจัดการกับข้อผิดพลาดในการปัดเศษทุกประเภทเมื่อคอมพิวเตอร์สามารถจัดเก็บส่วนทศนิยมของตัวเลขเป็นจำนวนเต็มอื่น (ซึ่งถูกต้องแล้ว)

สิ่งเดียวที่ฉันคิดได้ก็คือจัดการกับทศนิยมซ้ำ (ในฐาน 10) แต่อาจมีวิธีแก้ปัญหาที่เป็นไปได้ (เช่นที่เรามีอยู่กับอนันต์)


8
คุณควรตรวจสอบว่ามีการจัดเก็บประเภททศนิยมอย่างไรตรงกันข้ามกับประเภททศนิยม / แบบลอย
Oded

9
ไม่รู้ว่ามันแม่นยำกว่านี้อีกไหม ตัวเลขแรกหลังจุดทศนิยมคือ 1/10 สอง 1/100 เป็นต้นความแม่นยำที่มากขึ้นที่คุณยังคงได้รับคือปัญหาการปัดเศษ (คุณแทน 1/3) อย่างไร ความแตกต่างเพียงอย่างเดียวคือค่าที่สามารถแสดงได้อย่างแม่นยำ
Martin York

17
ทศนิยมทศนิยม (ซึ่งเป็นสิ่งที่คุณอ้างถึงสองเพียงในการเป็นตัวแทนที่น่าอึดอัดใจมากขึ้น) จะไม่ถูกต้องกว่าจุดลอยตัวไบนารี ความแตกต่างเพียงอย่างเดียวคือไม่สามารถแสดงค่าได้และเนื่องจากเราคุ้นเคยกับระบบทศนิยมเราจึงไม่สังเกตเห็นข้อผิดพลาดของเวอร์ชันทศนิยม และไม่สามารถเป็นตัวแทนของตัวเลขที่มีเหตุผลและไม่มีเหตุผลได้ทั้งหมด

1
ในตอนท้ายของวันมันเดือดลงไปมีประสิทธิภาพ คอมพิวเตอร์เป็นแบบไบนารี่และวงจรที่ใช้แทนไบนารี่นี้นั้นมีความซับซ้อนน้อยกว่ามาก ความสำคัญของสิ่งนี้อาจลดลงบ้างในทุกวันนี้ แต่นั่นเป็นเวลาที่สิ่งนี้สำคัญมาก นอกจากนี้การเป็นตัวแทนใด ๆ ที่คุณเลือกที่จะเก็บหมายเลขของคุณ (ในพื้นที่ จำกัด ) บนคอมพิวเตอร์จะมีชุดของค่าที่สามารถแสดงได้และพวกเขาทั้งหมดจะแสดงข้อผิดพลาดในการปัดเศษด้วยอินพุตบางส่วน รูปแบบจุดลอยทั่วไปที่มี Mantissa และ Exponent เสนอช่วงที่กว้างกว่านั้นจะเป็นไปได้โดยใช้จำนวนเต็มสองจำนวน
Mr.Mindor

1
ฉันขอแนะนำให้อ่านบทความที่อ้างอิงในคำตอบของคำถามที่ทำให้เกิดข้อผิดพลาดในการปัดเศษทศนิยม? ที่ฉันเพิ่งปรับปรุงพร้อมรายละเอียดของบทความล่าสุดในชุดอ้างอิง โดยเฉพาะอย่างยิ่งดูที่ทำไมจุดคงที่จะไม่รักษาจุดลอยน้ำสีน้ำเงินของคุณ
Mark Booth

คำตอบ:


35

จริงๆแล้วมีโหมดตัวเลขที่ทำเช่นนั้น

เลขฐานสิบฐานสอง (BCD) เลขคณิตมีคอมพิวเตอร์ทำงานอยู่ในฐาน 10 เหตุผลที่คุณพบบ่อยครั้งนี้คือมันเปลืองเนื้อที่: แต่ละหลักของตัวเลขแต่ละจำนวนจะใช้เวลาอย่างน้อยสี่บิตในขณะที่คอมพิวเตอร์สามารถเก็บได้มากถึง 16 ค่าในพื้นที่นั้น (อาจช้าลงได้ แต่เป็นไปได้ที่จะมี BCD คณิตศาสตร์ที่เร่งด้วยฮาร์ดแวร์ที่ทำงานได้ดี) นี่คือความจริงแล้วสิ่งที่เครื่องคำนวณส่วนใหญ่ทำซึ่งเป็นสาเหตุที่ทำให้เกิดปัญหาการปัดเศษบางประเภทที่คุณจะไม่เคยได้รับจาก Casio $ 5 ที่จะกินอาหารกลางวันบนคอมพิวเตอร์เดสก์ท็อป

เส้นทางอื่น ๆ ที่คุณสามารถใช้คือการใช้จำนวนตรรกยะ - นั่นคือตัวเศษและส่วนซึ่งจัดเก็บเป็นจำนวนเต็ม สิ่งนี้มีอยู่ในเกือบทุกภาษาแน่นอนและช่วยให้คุณเก็บทุกอย่างในรูปแบบไบนารีดั้งเดิม ปัญหาคือในตอนท้ายของวันผู้ใช้อาจไม่ต้องการเห็นเศษส่วนเช่น 463/13 หรือ 35 และ 8/13 พวกเขาต้องการเห็น 35.615 ... และเมื่อคุณไปถึงที่นั่นคุณต้องเผชิญกับปัญหาทั่วไปทั้งหมด เพิ่มในรูปแบบนี้ใช้พื้นที่มากขึ้นและอาจช้ากว่าเลขทศนิยมอย่างมีนัยสำคัญและคุณจะไม่พบคอมพิวเตอร์ที่ใช้รูปแบบนี้เป็นค่าเริ่มต้น

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


คุณไม่ได้หมายถึงสี่บิต (ไม่ใช่ไบต์) ในวรรค BCD ใช่ไหม

3
ตัวเลือกอื่นคือเลขคณิตจุดคงที่ซึ่งจำนวนเต็มแทนเศษส่วนทศนิยมถ้าตัวเลข - เช่นการเก็บค่าเงิน (โดยไม่ต้องคำนวณที่เกี่ยวข้องกับทศนิยมหรือเปอร์เซ็นต์) โดยที่ 1 หมายถึง $ 0.01
mattnz

1
@mattnz: True— จุดคงที่เป็นกรณีพิเศษของการปันส่วน
Jon Purdy

ยอดเยี่ยมไม่รู้ว่าเครื่องคิดเลขทำแบบนั้น
ลูกแมวบางคน

3
มีตัวเลือกที่สามคือ ทศนิยมที่มีเลขชี้กำลังเลขทศนิยมเช่นเดียวกับวิธีการใช้งาน C # decimal: stackoverflow.com/a/5019178/174335ไม่ใช่ BCD เนื่องจากไม่มีการแสดงทศนิยมแต่ละหลักและไม่ใช่จุดคงที่
Joren

38

มีวิธีมากมายในการจัดเก็บตัวเลขเศษส่วนและแต่ละวิธีมีข้อดีและข้อเสีย

รูปแบบจุดลอยตัวคือรูปแบบที่นิยมมากที่สุด มันทำงานได้โดยการเข้ารหัสสัญญาณ, mantissa และเลขชี้กำลังฐาน 2 ที่ลงนามเป็นจำนวนเต็มและบรรจุลงในกลุ่มบิต ตัวอย่างเช่นคุณสามารถมี mantissa แบบ 32 บิตของ0.5(เข้ารหัสเป็น0x88888888) และเลขชี้กำลังแบบ 32 บิตของ+3( 0x00000003) ซึ่งจะถอดรหัสเป็น4.0(0.5 * 2 ^ 3) จำนวนจุดลอยตัวนั้นรวดเร็วเนื่องจากมีการใช้งานในฮาร์ดแวร์และเครื่องชั่งที่มีความแม่นยำด้วยขนาดที่แน่นอนยิ่งจำนวนที่น้อยลงความแม่นยำสัมบูรณ์ของคุณก็จะยิ่งดีขึ้นเท่านั้น โฟลตนั้นยอดเยี่ยมสำหรับค่าตัวอย่างจากโดเมนต่อเนื่องเช่นความยาวระดับความดันเสียงระดับแสง ฯลฯ และด้วยเหตุนี้จึงใช้ในการประมวลผลเสียงและภาพรวมถึงการวิเคราะห์ทางสถิติและการจำลองทางฟิสิกส์ ข้อเสียที่ใหญ่ที่สุดของพวกเขาคือพวกเขาไม่ถูกต้องกล่าวคือพวกเขามีแนวโน้มที่จะปัดเศษข้อผิดพลาดและพวกเขาไม่สามารถแสดงเศษส่วนทศนิยมทั้งหมดได้อย่างถูกต้อง ภาษาโปรแกรมกระแสหลักทั้งหมดมีจุดลอยตัวของการจัดเรียงบางอย่าง

จุดคงที่ทำงานโดยใช้จำนวนเต็มขนาดใหญ่เพียงพอและสำรองส่วนหนึ่งโดยนัยของบิตสำหรับส่วนที่เป็นเศษส่วน ตัวอย่างเช่นหมายเลขจุดคงที่ 24.8 บิตสำรอง 24 บิตสำหรับส่วนจำนวนเต็ม (รวมถึงเครื่องหมาย) และ 8 บิตสำหรับส่วนที่เป็นเศษส่วน การขยับตัวเลขนั้นทางขวา 8 บิตจะทำให้เราได้ส่วนจำนวนเต็ม หมายเลขจุดคงที่เคยได้รับความนิยมเมื่อหน่วยจุดลอยตัวของฮาร์ดแวร์ผิดปกติหรืออย่างน้อยก็ช้ากว่าจำนวนเต็ม ในขณะที่ตัวเลขจุดคงที่ค่อนข้างง่ายต่อการจัดการในแง่ของความถูกต้อง (ถ้าเพียงเพราะพวกเขามีเหตุผลที่ง่ายกว่า) พวกเขาด้อยกว่าที่จะลอยตัวในเรื่องอื่น ๆ ทุกเรื่อง - พวกเขามีความแม่นยำน้อยกว่า การดำเนินการจำเป็นสำหรับการคำนวณที่ถูกต้องสำหรับการเปลี่ยนแปลงทางอ้อมคณิตศาสตร์จุดคงที่วันนี้มักจะช้ากว่าคณิตศาสตร์จุดลอย

ประเภททศนิยมทำงานเหมือนลอยหรือตัวเลขจุดคงที่ แต่พวกเขาถือว่าระบบทศนิยมนั่นคือเลขชี้กำลัง (โดยปริยายหรือโดยชัดแจ้ง) จะเข้ารหัสพลังงานของ 10 ไม่ใช่พลังงานของ 2 ยกตัวอย่างเช่นตัวเลขทศนิยมสามารถเข้ารหัส mantissa ของ23456และเลขชี้กำลังของ-2และสิ่งนี้จะขยายไป234.56. ทศนิยมเนื่องจากเลขคณิตไม่ได้ถูกต่อสายเข้ากับซีพียูช้ากว่าการลอย แต่มันเหมาะสำหรับทุกสิ่งที่เกี่ยวข้องกับตัวเลขทศนิยมและต้องการตัวเลขเหล่านั้นที่แน่นอนด้วยการปัดเศษในจุดที่กำหนดไว้อย่างดี กระดานบอกคะแนน ฯลฯ ภาษาการเขียนโปรแกรมบางภาษามีประเภททศนิยมอยู่ในตัว (เช่น C #) ส่วนภาษาอื่น ๆ ต้องการไลบรารีเพื่อใช้งาน โปรดทราบว่าในขณะที่ทศนิยมสามารถแสดงทศนิยมเศษส่วนที่ไม่ซ้ำได้อย่างแม่นยำ แต่ความแม่นยำนั้นไม่ได้ดีไปกว่าตัวเลขทศนิยม การเลือกทศนิยมหมายความว่าคุณได้รับตัวเลขที่แน่นอนซึ่งสามารถแสดงได้อย่างชัดเจนในระบบทศนิยม (เช่นเดียวกับการลอยตัวสามารถแสดงถึงเศษส่วนไบนารีได้อย่างแน่นอน)

จำนวนตรรกยะจัดเก็บเศษและเศษโดยปกติจะใช้ประเภทจำนวนเต็มจำนวน bignum (ประเภทตัวเลขที่สามารถเติบโตได้มากตามที่ข้อ จำกัด หน่วยความจำของคอมพิวเตอร์อนุญาต) นี่เป็นชนิดข้อมูลเดียวในเครือที่สามารถจำลองแบบตัวเลขเช่น1/3หรือ3/17ดำเนินการกับพวกมันได้อย่างถูกต้อง- ปันส่วนไม่เหมือนชนิดข้อมูลอื่น ๆ จะให้ผลลัพธ์ที่ถูกต้องสำหรับสิ่งต่าง ๆ เช่น3 * 1/3. คณิตศาสตร์ค่อนข้างตรงไปตรงมาแม้ว่าการหาอัลกอริทึมการแฟ็กเตอริงที่มีประสิทธิภาพนั้นค่อนข้างท้าทาย ภาษาการเขียนโปรแกรมบางภาษามีประเภทแบบมีเหตุผลอยู่ภายใน (เช่น Common LISP) ข้อเสียของการปันส่วนรวมถึงพวกเขาช้า (การดำเนินการจำนวนมากต้องการลดเศษส่วนและแยกองค์ประกอบของพวกเขา) และการดำเนินงานทั่วไปจำนวนมากนั้นยากหรือเป็นไปไม่ได้ที่จะนำมาใช้และการใช้งานส่วนใหญ่จะลดsin()บนเหตุผล)

BCD (Binary Coded Decimal) ใช้ "nibbles" (กลุ่มละ 4 บิต) เพื่อเข้ารหัสตัวเลขแต่ละตัว เนื่องจากแทะสามารถเก็บค่าที่แตกต่างกัน 16 ค่า แต่ตัวเลขฐานสิบต้องมีเพียง 10 ค่าจึงมีค่า "ผิดกฎหมาย" 6 ค่าต่อแทบ เช่นเดียวกับทศนิยมตัวเลข BCD เป็นทศนิยมที่แน่นอนนั่นคือการคำนวณที่ดำเนินการกับตัวเลขทศนิยมนั้นจะออกมาเช่นเดียวกับที่พวกเขาทำหากคุณใช้ปากกาและกระดาษ กฎทางคณิตศาสตร์ของ BCD ค่อนข้างงุ่มง่าม แต่ข้อเสียคือการแปลงสตริงเป็นสตริงทำได้ง่ายกว่ารูปแบบอื่น ๆ ซึ่งน่าสนใจเป็นพิเศษสำหรับสภาพแวดล้อมที่มีทรัพยากรต่ำเช่นระบบฝังตัว

สตริง , ใช่, สตริงเก่าธรรมดาสามารถใช้แทนตัวเลขเศษส่วนได้ ในทางเทคนิคแล้วสิ่งนี้คล้ายกับ BCD เพียงอย่างเดียวที่มีจุดทศนิยมชัดเจนและคุณใช้หนึ่งไบต์เต็มต่อหนึ่งหลักทศนิยม ดังนั้นรูปแบบจึงสิ้นเปลือง (ใช้ค่าที่เป็นไปได้เพียง 11 จาก 256 เท่านั้น) แต่ง่ายต่อการแยกวิเคราะห์และสร้างกว่า BCD นอกจากนี้เนื่องจากค่าที่ใช้ทั้งหมดนั้นเป็น "ไม่น่าสงสัย", ไม่เป็นอันตรายและไม่มีการเข้ารหัสสตริงที่เป็นกลางและเป็นแพลตฟอร์มสามารถเดินทางผ่านเครือข่ายได้โดยไม่มีปัญหา มันเป็นเรื่องแปลกที่จะหาเลขคณิตที่กระทำกับสตริงโดยตรง แต่เป็นไปได้และเมื่อคุณทำมันจะเป็นทศนิยมที่แน่นอนเท่ากับรูปแบบทศนิยมอื่น ๆ (ทศนิยมและ BCD)


แน่นอนจุดคงที่ 32 บิตมีความแม่นยำมากกว่าจุดลอยตัว 32 บิตเนื่องจากการแสดงจุดคงที่ไม่รวมแมนทิสซา
ฮัน

4
@han: ขึ้นอยู่กับขนาดของหมายเลขที่คุณต้องการจัดเก็บ ทุ่นลอย (จะประมาณ) ให้ความแม่นยำเดียวกันกับคุณไม่ว่าจำนวนจะมากหรือน้อยในขณะที่จุดคงที่จะให้ความแม่นยำเต็มที่ถ้าจำนวนที่คุณต้องการจัดเก็บอยู่ในช่วงพอดี
Leo

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

@han: พวกเขามีความแม่นยำเหมือนกัน (หรือเกือบ) ความแตกต่างคือสำหรับหมายเลขจุดคงที่ความแม่นยำ (ตามขนาดของขั้นตอนไม่ต่อเนื่องจากหมายเลขหนึ่งไปยังตัวตายตัวแทน) นั้นคงที่เช่นเดียวกับจำนวนเต็ม หมายเลข 1.0 มีความแม่นยำมากกว่าตัวเลข 10,000,000.0 (มากกว่าหนึ่งล้านเท่าโดยประมาณ)
tdammers

6

ตัวเลขจุดลอยตัวแสดงค่าที่หลากหลายซึ่งมีประโยชน์มากเมื่อคุณไม่ทราบล่วงหน้าว่าควรใช้ค่าอะไร แต่จะเป็นการประนีประนอม การเป็นตัวแทน 1/10 ^ 100 ด้วยจำนวนเต็มที่สองจะไม่ทำงาน

บางภาษา (และบางไลบรารี) มีคุณสมบัติอื่น เสียงกระเพื่อมมีจำนวนเต็มความแม่นยำไม่มีที่สิ้นสุด Cobol มีการคำนวณด้วยตัวเลขทศนิยมแบบตายตัว

คุณต้องเลือกการแสดงตัวเลขที่เหมาะสมกับโดเมนปัญหา


1

ดูเหมือนว่าคุณกำลังอธิบายหมายเลขจุดคงที่

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

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


1

นั่นจะเรียกว่า BCD ฉันคิดว่าคุณยังคงสามารถใช้งานได้หากคุณต้องการจริงๆ อย่างไรก็ตามมันไม่คุ้มกับ:

  1. คุณจะไม่ค่อยพบข้อผิดพลาดในการปัดเศษด้วยคะแนนทศนิยม 64 บิต
  2. มันทำให้ความซับซ้อนและไม่มีประสิทธิภาพทางคณิตศาสตร์
  3. มันเสียค่า 6 ทุก 4 บิต

คณิตศาสตร์ BCD ถูกใช้บ่อยในระบบไมโครโปรเซสเซอร์ 8 บิต อันที่จริงแล้วบนไมโครโปรเซสเซอร์ที่ได้รับความนิยมหนึ่งเดียว (ในรุ่น 6502) การเพิ่มและการลบด้วย BCD นั้นเร็วกว่าไบต์ต่อไบต์เช่นเดียวกับไบนารี วิดีโอเกมใช้คณิตศาสตร์ BCD เป็นประจำในการเก็บคะแนน ไม่มีการจัดการพิเศษสำหรับการห่อคะแนน 1,000,000 คะแนน ให้เพิ่ม 1 ถึง "99 99 99" แทน "00 00 00" ด้วยการพกซึ่งจะถูกละเว้น ค่าใช้จ่ายเพิ่มเติมในการเพิ่มคะแนนที่ BCD นั้นเล็กน้อยเมื่อเทียบกับค่าใช้จ่ายในการแปลงค่าไบนารี่เป็นรูปแบบที่แสดงได้
supercat

1

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

ซึ่งมีแนวโน้มที่จะได้รับการสนับสนุนในฮาร์ดแวร์ส่วนใหญ่เนื่องจากการคำนวณทางวิทยาศาสตร์มีแนวโน้มที่จะได้รับประโยชน์สูงสุดจากการสนับสนุนฮาร์ดแวร์ ตัวอย่างหนึ่งการคำนวณทางการเงินมักทำกับรูปแบบอื่น ๆ - แต่ซอฟต์แวร์ทางการเงินมักจะทำการคำนวณจริงเล็กน้อยพอที่แม้ว่ารูปแบบที่จำเป็นจะได้รับการสนับสนุนในซอฟต์แวร์เท่านั้น แต่ประสิทธิภาพยังคงเพียงพอสำหรับซอฟต์แวร์ทางการเงินส่วนใหญ่

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