เช่นกับดอลลาร์คุณจะไม่มีความแม่นยำน้อยกว่า $ 0.01
โอ้จริงเหรอ?
อายุที่มากขึ้นว่าทำไมคุณไม่ควรเก็บสกุลเงินเป็นเลขทศนิยม IEEE 754
โปรดอย่าลังเลที่จะเก็บนิ้วในIEEE 754 ตัวเลขทศนิยม พวกเขาจัดเก็บอย่างแม่นยำตามที่คุณคาดหวัง
โปรดเก็บจำนวนเงินใด ๆ ในหมายเลขทศนิยมของIEEE 754ที่คุณสามารถจัดเก็บได้โดยใช้เห็บที่แบ่งไม้บรรทัดออกเป็นเสี้ยวนิ้ว
ทำไม? เพราะเมื่อคุณใช้IEEE 754นั่นเป็นวิธีที่คุณเก็บไว้
สิ่งที่เกี่ยวกับนิ้วคือพวกเขาแบ่งครึ่ง สิ่งที่เกี่ยวกับสกุลเงินส่วนใหญ่คือพวกมันถูกแบ่งออกเป็นสิบส่วน
ความแตกต่างนี้จะไม่เกิดความสับสนยกเว้นว่าสำหรับภาษาการเขียนโปรแกรมส่วนใหญ่อินพุตและเอาต์พุตจากตัวเลขทศนิยมIEEE 754จะแสดงเป็นทศนิยม! ซึ่งแปลกมากเพราะพวกเขาไม่ได้ถูกเก็บไว้ในตำแหน่งทศนิยม
ด้วยเหตุนี้คุณไม่เคยได้เห็นวิธีการบิตทำสิ่งแปลก0.1
เมื่อคุณถามคอมพิวเตอร์ในการจัดเก็บ คุณจะเห็นความแปลกประหลาดเมื่อคุณทำคณิตศาสตร์กับมันและมีข้อผิดพลาดแปลก ๆ
จากจาวาที่มีประสิทธิภาพของ Josh Bloch :
System.out.println(1.03 - .42);
ผลิต 0.6100000000000001
สิ่งที่คนส่วนใหญ่บอกเกี่ยวกับสิ่งนี้ไม่ใช่1
วิธีนั่งอยู่ตรงนั้นทางด้านขวา มันเป็นตัวเลขประหลาดที่ต้องใช้เพื่อให้ได้มา แทนที่จะใช้ตัวอย่างที่เป็นที่นิยมที่สุด0.1
เราต้องใช้ตัวอย่างที่แสดงปัญหาและหลีกเลี่ยงการปัดเศษที่จะซ่อนมัน
ตัวอย่างเช่นเหตุใดจึงใช้งานได้
System.out.println(.01 - .02);
ผลิต -0.01
เพราะเราโชคดี
ฉันเกลียดปัญหาที่วินิจฉัยยากเพราะบางครั้งฉันก็ "โชคดี"
IEEE 754ไม่สามารถเก็บได้อย่างแม่นยำ 0.1 แต่ถ้าคุณขอให้เก็บ 0.1 แล้วขอให้พิมพ์มันจะแสดง 0.1 และคุณจะคิดว่าทุกอย่างเรียบร้อยดี มันไม่ดี แต่คุณไม่สามารถเห็นได้ว่ามันเป็นการปัดเศษเพื่อกลับไปที่ 0.1
บางคนสร้างความสับสนให้กับคนอื่นโดยเรียกความคลาดเคลื่อนเหล่านี้มาปัดเศษข้อผิดพลาด ไม่ข้อผิดพลาดเหล่านี้ไม่ใช่การปัดเศษ การปัดเศษกำลังทำสิ่งที่ควรจะเป็นและเปลี่ยนสิ่งที่ไม่ใช่ทศนิยมให้เป็นทศนิยมเพื่อให้สามารถพิมพ์บนหน้าจอได้
แต่สิ่งนี้จะซ่อนความไม่ตรงกันระหว่างวิธีแสดงหมายเลขและวิธีจัดเก็บ ข้อผิดพลาดจะไม่เกิดขึ้นเมื่อมีการปัดเศษ มันเกิดขึ้นเมื่อคุณตัดสินใจที่จะใส่ตัวเลขลงในระบบที่ไม่สามารถจัดเก็บได้อย่างแม่นยำและคิดว่ามันถูกเก็บไว้อย่างแม่นยำเมื่อมันไม่ได้เป็น
ไม่มีใครคาดว่า store จะจัดเก็บไว้อย่างแม่นยำในเครื่องคิดเลขและพวกเขาก็สามารถทำงานได้ด้วยดี ดังนั้นปัญหาไม่ได้เกี่ยวกับความแม่นยำ มันเกี่ยวกับความแม่นยำที่คาดหวัง คอมพิวเตอร์แสดงหนึ่งในสิบเช่น0.1
เดียวกับเครื่องคิดเลขของเราดังนั้นเราคาดว่าพวกเขาจะเก็บหนึ่งในสิบอย่างสมบูรณ์แบบที่เครื่องคิดเลขของเราทำ พวกเขาทำไม่ได้ ซึ่งน่าแปลกใจเนื่องจากคอมพิวเตอร์มีราคาแพงกว่า
ผมขอแสดงความไม่ตรงกันให้คุณดู:
สังเกตเห็นว่า 1/2 และ 0.5 เข้ากันได้อย่างสมบูรณ์ แต่ 0.1 ไม่ได้เข้าแถว แน่นอนว่าคุณสามารถเข้าใกล้มากขึ้นถ้าคุณหารด้วย 2 ต่อไป แต่คุณจะไม่ตีมันแน่นอน และเราต้องการบิตเพิ่มมากขึ้นทุกครั้งที่เราหารด้วย 2 ดังนั้นการแทน 0.1 ด้วยระบบใด ๆ ที่หารด้วย 2 ต้องการจำนวนบิตไม่ จำกัด ฮาร์ดไดรฟ์ของฉันไม่ใหญ่มาก
ดังนั้นIEEE 754หยุดพยายามเมื่อมันหมดบิต ซึ่งเป็นสิ่งที่ดีเพราะฉันต้องการห้องบนฮาร์ดไดรฟ์สำหรับ ... รูปถ่ายครอบครัว ไม่มีจริงๆ. ภาพถ่ายครอบครัว : P
อย่างไรก็ตามสิ่งที่คุณพิมพ์และสิ่งที่คุณเห็นคือทศนิยม (ด้านขวา) แต่สิ่งที่คุณจัดเก็บคือจักรยาน (ทางซ้าย) บางครั้งก็เหมือนกันอย่างสมบูรณ์แบบ บางครั้งพวกเขาไม่ได้ บางครั้งดูเหมือนว่าพวกเขาเหมือนกันเมื่อพวกเขาไม่ได้ นั่นคือการปัดเศษ
โดยเฉพาะอย่างยิ่งเราต้องรู้อะไรบ้างเพื่อให้สามารถเก็บค่าในบางสกุลเงินและพิมพ์ออกมาได้
กรุณาถ้าคุณจัดการเงินตามทศนิยมของฉันอย่าใช้ทุ่นหรือดับเบิล
หากคุณแน่ใจว่าสิ่งต่างๆเช่นเศษสิบของเพนนีจะไม่เกี่ยวข้องก็ให้เก็บเงินไว้ หากคุณไม่ได้คิดออกว่าหน่วยที่เล็กที่สุดของสกุลเงินนี้จะเป็นเท่าไหร่และใช้หน่วยนั้น ถ้าคุณไม่สามารถใช้สิ่งที่ต้องการBigDecimal
มูลค่าสุทธิของฉันอาจจะพอดีกับจำนวนเต็ม 64 บิตเสมอ แต่สิ่งต่าง ๆ เช่นBigIntegerทำงานได้ดีสำหรับโครงการที่ใหญ่กว่านั้น พวกมันช้ากว่าประเภทพื้นเมือง
การหาวิธีจัดเก็บเป็นเพียงครึ่งหนึ่งของปัญหา จำไว้ว่าคุณต้องแสดงมันด้วย การออกแบบที่ดีจะแยกสองสิ่งนี้ออกจากกัน ปัญหาที่แท้จริงของการใช้โฟลทที่นี่คือสองสิ่งที่ถูกรุมด้วยกัน