Tags:

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


print (0.05 + 0.05 == 0.1)
... True

เมื่อการทำงานในขั้นต้นทำงานดี โปรแกรมเมอร์อาจจะเชื่อว่าส่วนนี้ทำงานถูกต้องดี จนกระทั่งการทำงานในแบบที่คล้ายๆ กันมีการทำซ้ำจำนวนมากๆ เช่น


d1 = 0.0
for i in range(1000):
d1 += 0.0001
print repr(d1)
... 1.0000000000000007
print (d1 == 1.0)
... False

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

ใน Python 2.4 เป็นต้นมา มีการอิมพลีเมนต์เอกสาร PEP-327 ซึ่งเป็นสเปคของเลขทศนิยมฐานสิบ ที่ทำงานในส่วนนี้ได้ถูกต้องกว่า อย่างในตัวอย่างต่อไปนี้


dec1 = Decimal("0.0");
for i in range(1000):
dec1 += Decimal("0.001")
print repr(dec1)
... Decimal("1.000")
print (dec1 == Decimal("1.0"))
... True

เท่านี้เราก็จะได้การทำงานที่เป็นไปตามคาดโดยไม่ต้องกังวลความไม่แน่นอนของโปรแกรมอีกต่อไป

Get latest news from Blognone

Comments

By: sake on 12 May 2006 - 09:56 #6720

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

มีคนเสนอให้ python มีเลขจำนวนจริงใน PEP-239, 240 แต่ข้อเสนอถูก reject ไปด้วยเหตุผลเรื่อง computational resource ครับ

""" The performance implications of the fact that summing two rationals (which take O(M) and O(N) space respectively) gives a rational which takes O(M+N) memory space is just too troublesome. There are excellent Rational implementations in both pure Python and as extensions (e.g., gmpy), but they'll always be a "niche market" IMHO. Probably worth PEPping, not worth doing without Decimal -- which is the right way to represent sums of money, a truly major use case in the real world. """

และในงานที่มีการคำนวนเยอะๆและต้องการความรวดเร็ว ตัวเลขแบบ Float จะดีกว่า Decimal มากเพราะใช้ความสามารถของ FPU ได้เต็มที่ นอกจากนั้น Decimal ของ python ในตอนนี้ยัง implement ด้วย python เอง ซึ่งทำให้อืดมาก ซึ่งในอนาคตเค้าก็มีแผนจะ implement ด้วย c แทน

Decimal น่าจะมีประโยชน์กับคนทำงานกับ database มากกว่าคนทำงานวิทยาศาสตร์ครับ เพราะชัวร์กว่า (แต่ช้ากว่า)

By: lew
FounderJusci's WriterMEconomicsAndroid
on 13 May 2006 - 21:24 #6734
lew's picture

sake - rational คือจำนวนตรรกยะครับ ไม่ใช่จำนวนจริง


lewcpe.com, @public_lewcpe

By: lew
FounderJusci's WriterMEconomicsAndroid
on 13 May 2006 - 21:35 #6736
lew's picture

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


lewcpe.com, @public_lewcpe

By: sake on 16 May 2006 - 10:33 #6788

ใช่ๆครับ จำผิด จำนวนจริง คือจำนวนเต็มบวก จำนวนตรรกยะคือจำนวนที่เขียนในรูปเศษส่วนได้

ถ้าเข้าใจไม่ผิดจำนวนอตรรกยะเช่น Pi มักจะมีค่าอยู่ในรูปของสมการนะครับไม่มีใครหาตัวเลขแน่นอนได้ คงไม่ใช่ปัญหาเฉพาะของ computer เป็นปัญหากับคนด้วยครับ

By: cwt
AndroidRed Hat
on 2 August 2006 - 04:32 #8403

ขอเสริมหน่อยนะครับ ของผมใช้ ActivePython นะ มันต้อง ใส่

from decimal import Decimal

ก่อนน่ะ ถึงจะมี Decimal ให้ใช้ ไม่รู้ว่าใน Python ปกติ (จาก python.org) ต้อง import ก่อนหรือเปล่า พอดีเห็นใน code ตัวอย่าง มาถึงก็เรียกใช้เอาเลย เล่นเอาผมงงไปพักใหญ่ :-P

By: lew
FounderJusci's WriterMEconomicsAndroid
on 6 August 2006 - 01:31 #8518
lew's picture

เป็นเหมือนกันทุกที่ครับ พอดีบรรทัด import ผมละไป ------ LewCPE


lewcpe.com, @public_lewcpe

By: Humanware on 10 December 2006 - 20:13 #14117

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