ปัญหาตัวเลขในการโค้ดดิ่ง

 

หนึ่งในปัญหาคลาสิก เวลาเขียนโปรแกรมที่ทุกคนต้องเจอเลย
ก็คือการบวกลบเลขทศนิยมในภาษาโปรแกรม ของบางภาษา นี้แหละ
เช่น JavaScript, Python, Perl, C#, C, C++, Java, PHP, Fortran
(และอื่นๆ อีกหลายภาษาที่ไม่ได้กล่าวถึง)
.
หลายครั้งที่มันอาจเพี้ยนได้ เช่น
? 0.1+0.2 ไม่ได้เท่ากับ 0.3
แต่ได้เป็น 0.30000000000000004
.
? หรือ 0.1 บวกกัน 10 ครั้ง ก็ไม่ได้เป็น 1
แต่ได้เป็น 0.9999999999999999
.
คนเขียนโปรแกรมเจอแบบนี้เข้าไป
ก็เหมือนมวยโดนหมัดน๊อคมึนงงในดงโค้ด
:
:
แต่ใช่ว่ามันจะเพี้ยนทุกครั้ง ซะเมื่อไร เช่น
0.5+0.5 = 1 (ถูกต้องเป๊ะ)
0.2+0.3 = 0.5 (บังเอิญไม่เพี้ยน)
.
สำหรับ กรณี 0.2 กับ 0.3 มันถูกตัดเศษเหลือเป็น
0.2000000000000000111022302462515654042363166809082031250
กับ
0.2999999999999999888977697537484345957636833190917968750
พอบวกกันจึงได้ 0.5 พอดี แบบฟลุ๊คๆๆ ซึ่งไม่ควรทำได้
(ตรงสอบดูได้ 0.2+0.3 == 0.5 ได้ค่าออกมาเป็น true)
:
:
สาเหตุที่เป็นเช่นนี้
ก็เพราะว่าคอมพิวเตอร์มันรู้จักแต่ เลขฐาน2 อะนะ
ต่อให้เราเขียนโค้ดใช้เลขฐาน10 ก็ตาม
สุดท้ายเวลาโค้ดมันถูกรัน ก็จะกลายเป็นเลขฐาน 2 อยู่ดี
.
? แล้วก็เป็นความซวยที่จะมาเยือนคนเขียนโปรแกรม
เพราะเวลาแปลงเลขฐาน10 ไปเป็นเลขฐาน 2
บางกรณีมันแปลงแล้ว ดันได้ตัวเลขที่ไม่รู้จบเสียด้วยซิ
จึงทำให้การเก็บทศนิยมผิดเพี้ยนไปได้
.
สำหรับรูปแบบการจัดเก็บเลขทศนิยม ในหลายภาษา
เขาจะนิยมใช้มาตรฐาน IEEE-754 floating point
เช่น 0.1 จะถูกมองว่าคือ 1/10
.
เมื่อเก็บเป็นเลขทศนิยมฐานสอง
ตามมาตรฐาน IEEE-754 floating point จะได้เป็น
0.0001100110011001100110011001100110011001100110011…
เป็นทศนิยมไม่รู้จบในรูปเลขฐานสอง ….นี้คือสิ่งที่คอมมองเห็น
.
พอคอมแปลงกลับมาเป็นทศนิยม เพื่อให้มนุษย์โลกอ่านเข้าใจ
ในรูปฐาน 10 ก็จะได้เป็น
0.1000000000000000055511151231257827021181583404541015625
ทว่าคอมมันจะตัดให้เหลือแค่ 0.1 (คนจึงเห็นแค่นี้)
:
? ซึ่งความเพื้ยนแบบนี้
แน่นอนทำให้เกิดบั๊กเวลาคำนวณตัวเลข
– ยิ่งงานต้องการคำตอบที่ละเอียดมาก เช่น งานธนาคาร ก็จะประสบปัญหา เป็นต้น
– หรือเวลานำไปใช้ในเงื่อนไขเปรียบเทียบพวก if, while ฯลฯ ก็อาจมีบั๊กเกิดขึ้นได้ เป็นต้น
.
? แต่ไม่ต้องห่วง ในหลายๆ ภาษาเขาจะมีวิธีแก้ปัญหานี้อยู่ครับ
ป้องกันการคำนวณตัวเลข ไม่ให้คลาดเคลื่อน เช่น
– ใน Java ก็จะมีคลาส BigDecimal เอาไว้บวกลบคูณหาร สำหรับเลขทศนิยมโดยเฉพาะ
– ใน Python ก็จะมีคลาสคล้ายๆ กัน เช่น Decimal
– ส่วนใน JavaScript อาจใช้ไลบรารี่ ซึ่งมีให้เลือกเยอะเช่น
https://github.com/MikeMcl/decimal.js/
https://github.com/MikeMcl/bignumber.js/
https://github.com/MikeMcl/big.js/
– ภาษาอื่นที่เหลือลองไปศึกษาเองดูนะครับ
.
.
เรื่องบวกลบคูณหาร เลขทศนิยม ถือเป็นเรื่องสำคัญที่ไม่ควรมองข้าม
โดยส่วนตัวก็เคยเจอความเผลอเรอตรงนี้
ในระดับโปรเจคระดับธนาคาร ก็เคยพลาดมาแล้ว
สุดท้ายต้องมาไล่นั่งแก้โค้ดหลายบรรทัด
เสียเวลานั่งไล่ test ใหม่อีกรอบอีก
.
หมายเหตุเห็นคอมเมนต์สงสัยว่า
PHP กับ C# รอดชะตากรรมเดียวกันไหม ?
ก็บอกว่าไม่รอดครับ
.
// ลองดูตัวอย่างโค้ด C#
Console.WriteLine( ((0.1+0.2) == 0.3)); // False
Console.WriteLine( ((0.1+0.2) == 0.30000000000000004)); // True
// ลองดูตัวอย่างโค้ด PHP
echo number_format(0.1+0.2 , 17);
.
++++++
เขียนโดย โปรแกรมเมอร์ไทย thai programmer
อ่านเรื่อง IEEE-754 floating point ได้ที่
https://th.wikipedia.org/wiki/จำนวนจุดลอยตัว

 

 

?เขียนโดยแอดมินโฮ โอน้อยออก

Please like Fanpage