หัวข้อ 71/91· การพัฒนาซอฟต์แวร์และการให้บริการดิจิทัล

การรวมต้นทุนที่ปลอดภัยต่อสกุลเงิน

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

ทำไมจึงสำคัญ

นี่คือประเภทของบั๊กซอฟต์แวร์ที่มีเอกสารรองรับดีและเป็นพื้นฐาน: บทความของ Goldberg ปี 1991 ใน ACM Computing Surveys "What Every Computer Scientist Should Know About Floating-Point Arithmetic" เป็นเอกสารอ้างอิงมาตรฐานว่าทำไมทศนิยมลอยตัวแบบไบนารีจึงแทนค่าเงินทศนิยมส่วนใหญ่ไม่ได้อย่างแม่นยำ และทำไมการรวมค่าเหล่านั้นจำนวนมากจึงทบทวีความผิดพลาด แบบจำลองเศรษฐศาสตร์สุขภาพและการเงินของ NHS รวมหลายปีและหลายหมวดต้นทุนเป็นประจำ — ต้นทุนรวมของการเป็นเจ้าของและการวิเคราะห์ผลกระทบต่องบประมาณต่างก็รวมรายการต้นทุน f64 จำนวนมากตลอดช่วงเวลาหลายปี เมื่อแบบจำลองต้องลงตัวถึงเซ็นต์ — การตรวจสอบที่คำนวณยอดรวมด้วยมือต้องได้ตัวเลข เหมือนกันทุกประการ — เลขคณิตเองต้องเป็นทศนิยมที่แม่นยำ ไม่ใช่ทศนิยมลอยตัว

คณิตศาสตร์

การรวมแบบไร้เดียงสา:        ยอดรวม = Σ f64(รายการ_i)         — ความคลาดเคลื่อนที่ขึ้นกับลำดับ
การรวมที่ปลอดภัยต่อสกุลเงิน: ยอดรวม = Σ Decimal(รายการ_i)      — แม่นยำ ทำซ้ำได้

การใช้การปรับด้วยเปอร์เซ็นต์ (เช่น กันสำรอง):
  ปรับแล้ว = ยอดรวม × ตัวคูณ              — ผลลัพธ์ Decimal ที่แม่นยำ อาจมีทศนิยม
                                              มากกว่าเลขชี้กำลังหน่วยย่อยที่สุด
                                              ของสกุลเงิน
  ปัดแล้ว = ปัด(ปรับแล้ว, เลขชี้กำลังสกุลเงิน, กฎการปัด)  — กฎการปัด (half-up
                                              เทียบกับ half-even/การปัดแบบนายธนาคาร)
                                              ต้องระบุอย่างชัดเจน

สังเกตวินัยสองขั้นตอน: การคูณจำนวน Decimal ที่แม่นยำด้วยตัวคูณอาจให้ทศนิยมมากกว่าที่สกุลเงินใช้จริง (เช่น ทศนิยมสามตำแหน่งจากจำนวนที่มีสองตำแหน่งคูณตัวคูณที่มีสองตำแหน่ง) — ความแม่นยำระหว่างทางนั้น จะไม่ ถูกปัดทิ้งโดยอัตโนมัติ มีเพียงขั้นตอนการปัดที่ชัดเจนพร้อมกฎการปัดที่ระบุเท่านั้นที่ลดมันลงสู่เลขชี้กำลังหน่วยย่อยที่สุดที่แท้จริงของสกุลเงิน

ตัวอย่างที่คำนวณแล้ว

ใบแจ้งหนี้รายเดือนที่เหมือนกันสิบสองใบ ใบละ $1,234.56 รวมด้วยเลขคณิตทศนิยมที่แม่นยำ: $1,234.56 × 12 = $14,814.72 พอดี เทียบกับการรวมค่าคงที่ f64 1234.56 สิบสองครั้งในความแม่นยำสองเท่าตาม IEEE-754 ซึ่งอาจคลาดเคลื่อนเป็นเศษส่วนของเซ็นต์ขึ้นกับลำดับการรวม — ประเภทของบั๊กที่เกิดขึ้นจริงและมีเอกสารรองรับ ไม่ใช่ปัญหาสำหรับแบบจำลองที่สร้างบนเลขคณิต Money ทศนิยมที่แม่นยำ

ทีนี้ใช้กันสำรองผลกระทบต่องบประมาณมาตรฐาน 5% (ตัวคูณ 1.05) กับยอดรวม $14,814.72 นั้น: $14,814.72 × 1.05 = $15,555.456 — ทศนิยมสามตำแหน่ง เพราะการคูณแม่นยำและไม่ถูกปัดโดยอัตโนมัติเป็นสองตำแหน่งของสกุลเงิน เมื่อปัดเป็น 2 ตำแหน่งอย่างชัดเจนด้วยการปัดแบบนายธนาคาร (half-even) ได้ $15,555.46 พอดี

ความเชื่อมโยงกับวิศวกรรมซอฟต์แวร์

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

ข้อผิดพลาดที่พบบ่อย

  • แปลงเป็น float กลางห่วงโซ่: การดึงค่าเงินออกเป็นเลขทศนิยมลอยตัวระหว่างการคำนวณ (ไลบรารี Money บางตัวถึงกับตั้งชื่อเมธอดแปลงนี้ว่า "lossy" เป็นคำเตือนที่ชัดเจน) ทิ้งการรับประกันความแม่นยำอย่างเงียบๆ สำหรับทุกการคำนวณหลังจุดนั้น
  • "Decimal ช้าเกินกว่าจะสนใจ": การปัดตก เลขคณิตทศนิยมที่แม่นยำว่าเป็นภาระที่ไม่จำเป็น ทั้งที่การรายงานทางการเงินต้องการความถูกต้องและการตรวจสอบได้ ไม่ใช่ปริมาณงานดิบ
  • ใช้เปอร์เซ็นต์กันสำรองโดยไม่ระบุกฎการปัด: half-up เทียบกับ half-even (การปัดแบบนายธนาคาร) อาจเปลี่ยนเซ็นต์สุดท้าย; ข้อตกลงการปัดเองต้องเป็นทางเลือกที่ระบุไว้และตรวจสอบได้ — ดูการวิเคราะห์ต้นทุนผลประโยชน์สำหรับแนวทาง Green Book ของ HM Treasury เกี่ยวกับการปรับกันสำรองและอคติแง่ดี ซึ่งเป็นตัวเลขประเภทที่ขั้นตอนการปัดนี้นำไปใช้

แหล่งที่มา