แสดงบทความที่มีป้ายกำกับ เลขฐาน 10 แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ เลขฐาน 10 แสดงบทความทั้งหมด

วันอังคารที่ 27 พฤศจิกายน พ.ศ. 2561

จำนวนที่น้อยที่สุดที่เมื่อบวกกับ 1 แล้วได้ผลลัพธ์ไม่ใช่ 1 (การทำวิทยานิพนธ์ภาคปฏิบัติตอนที่ ๙๔) MO Memoir : Tuesday 27 November 2561

เวลาที่คอมพิวเตอร์เก็บค่าตัวเลขในรูปแบบเลขจำนวนจริง (Real) มันก็จะเก็บในทำนอง ±0.ddddd.. x 10±yyy (อันที่จริงมันเก็บในรูปแบบเลขฐาน 2 นะ แต่ขอเขียนในรูปแบบเลขฐาน 10 เพื่อให้มองเห็นภาพได้ง่าย) เมื่อ ddddd... คือตัวเลขที่อยู่หลังจุดทศนิยม ส่วน yyy ก็เป็นส่วนของเลขยกกำลัง และก็มีบิทที่เป็นตัวกำหนดเครื่องหมายของส่วน ddddd... ว่าจะให้เป็นบวกหรือลบ และส่วนของ yyy ว่าจะให้เป็นยกกำลังที่เป็นบวกหรือลบ ส่วนตัวเลขที่อยู่หลังจุดทศนิยมจะมีได้กี่ตำแหน่ง และ yyy จะมีค่าได้มากที่สุดเท่าใดนั้นก็ขึ้นอยู่กับจำนวนบิท (bit) ที่ใช้ในการเก็บข้อมูล ถ้าใช้บิทมากก็จะเก็บได้ละเอียดมาก แต่นั่นก็หมายถึงการสิ้นเปลืองหน่วยความจำมากขึ้น และการคำนวณที่กินระยะเวลายาวนานขึ้นด้วย
 
เวลาที่เราให้มันเก็บตัวเลขเช่น 12.34 มันก็จะเก็บเป็น 0.12340000 x 102 และถ้าให้เก็บตัวเลข 0.001234 มันก็จะเก็บค่าเป็น 0.12340000 x 10-2 กล่าวคือมันจะทำการปรับในส่วนของตัวเลขยกกำลัง เพื่อให้ตัวเลขตัวแรกที่อยู่หลังจุดทศนิยมนั้นเป็นตัวเลขที่ไม่ใช่ศูนย์ ส่วนตัวเลขที่อยู่หลังเลข 4 ก็จะมีค่าเป็นศูนย์ไปทั้งหมด ส่วนจะมีกี่ตัวนั้นก็ขึ้นอยู่กับว่ามันเก็บทิศนิยมได้กี่ตำแหน่ง อย่างเช่นในที่นี้สมมุติว่ามันเก็บได้แค่ 8 ตำแหน่ง ตัวเลขก็จะเป็นดังที่แสดง (คือมี 0 ต่อท้ายอีก 4 ตัว)
 
เวลาที่ทำการบวกเลขสองจำนวนเข้าด้วยกัน สิ่งที่คอมพิวเตอร์ทำก็คือการปรับให้ส่วนที่เป็นเลขยกกำลังของตัวเลขทั้งสองจำนวนนั้นเท่ากันก่อน ด้วยการเขยิบตัวเลขส่วนทศนิยมนั้นมาข้างหลัง (หรือเพิ่ม 0 ไปข้างหน้า) และเพิ่มตัวเลขในส่วนที่เป็นเลขยกกำลังให้สูงขึ้น (1 หน่วยทุก ๆ การเขยิบถอยหลัง 1 หลัก) อย่างเช่นถ้าเราต้องการบวก 12.34 เข้ากับ 0.001234 มันก็จะปรับ 12.34 เป็น 0.12340000 x 102 และปรับ 0.001234 เป็น 0.00001234 x 102 จากนั้นจึงบวกส่วนที่เป็นเลขทศนิยมเข้าด้วยกัน ผลที่ได้คือ 0.12341234 x 102 
 
ทีนี้ถ้าเราต้องการบวกตัวเลข 0.00001234 เข้ากับ 12.34 บ้าง เลข 0.00001234 ถูกเก็บเป็น 0.12340000 x 10-4 จากนั้นก็ทำการปรับให้ส่วนที่เป็นเลขยกกำลังเท่ากัน ในคราวนี้ 0.00001234 จะกลายเป็น 0.00000012 x 10-4 คือเลข 3 และ 4 สองตัวท้ายจะหายไปอันเป็นผลจากการที่มันเก็บทศนิยมไว้ได้เพียงแค่ 8 ตำแหน่ง ดังนั้นผลรวมที่ได้จึงกลายเป็น 0.12340012 x 102 ซึ่งมีความคลาดเคลื่อนไปจากคำตอบที่ควรเป็น
 
(หมายเหตุ : ในที่นี้เวลากล่าวถึงการ "บวก" ก็จะครอบคลุมถึงการ "ลบ" ด้วย และในทำนองเดียวกันเวลากล่าวถึงการ "คูณ" ก็จะครอบคลุมถึงการ "หาร" ด้วย)
 
แล้วปัญหาเรื่องนี้มันส่งผลต่อการคำนวณมากน้อยแค่ไหน ตรงนี้ก็ขึ้นอยู่กับว่าเราเขียนลำดับคำสั่งการคำนวณอย่างไร ตัวเช่นการคำนวณค่า 1 + e - 1, 1 - 1 + e และ 1 - (1 - e) เมื่อ e มีค่าต่าง ๆ กัน ซึ่งในทางทฤษฎีแล้วการเขียนทั้งสามรูปแบบควรให้คำตอบเดียวกัน แต่พอลองคำนวณด้วยโปรแกรม spread sheet ของ OpenOffice 4.1.5 กลับได้ผลดังแสดงในตารางที่ ๑ ซึ่งจะเห็นได้ว่าตั้งแต่ e มีค่าจาก 0.001 ไปจนถึง 10-14 นั้น ทั้งสามรูปแบบให้คำตอบที่แตกต่างกัน และเมื่อ e มีค่าตั้งแต่ 10-15 หรือต่ำลงไป การเขียนในรูปแบบ 1 + e - 1 และ 1 - (1 - e) ให้คำตอบที่เป็น 0 ในขณะที่การเชียนแบบ 1 - 1 + e นั้นยังให้คำตอบที่ถูกต้องอยู่
 
(หมายเหตุ : ผลการคำนวณที่ได้นั้นยังขึ้นอยู่กับคอมพิวเตอร์ที่ใช้ว่าเป็นระบบกี่บิท ดังนั้นถ้าลองทำกับเครื่องที่ตนเองใช้อยู่แล้วได้ผลที่ไม่เหมือนที่นำมาแสดงนี้ก็ไม่ใช่เรื่องแปลก แต่ถ้าใช้บิทเยอะ (เช่น 64 บิท) จะเห็นปัญหาเช่นนี้เกิดขึ้นช้ากว่ากรณีของเครื่อง 16 บิทหรือ 32 บิท)
 
ตารางที่ ๑ ผลการคำนวณค่า 1 + e - 1, 1 - 1 + e และ 1 - (1 - e) เมื่อ e มีค่าต่าง ๆ กัน การคำนวณนี้กระทำบนโปรแกรม spread sheet ของ OpenOffice 4.1.5 ในทางทฤษฎีแล้วมันควรจะให้ค่าที่เท่ากัน แต่ในทางปฏิบัติเมื่อทำการคำนวณด้วยเครื่องดิจิตอลคอมพิวเตอร์จะเห็นว่าค่าที่ได้นั้นแตกต่างกันอยู่

e
1 + e - 1
1 - 1 + e
1 - (1 - e)
1.00E-01
0.10000000000000000000
0.10000000000000000000
0.10000000000000000000
1.00E-02
0.01000000000000000000
0.01000000000000000000
0.01000000000000000000
1.00E-03
0.00099999999999989000
0.00100000000000000000
0.00100000000000000000
1.00E-04
0.00009999999999998900
0.00010000000000000000
0.00009999999999998900
1.00E-05
0.00001000000000006550
0.00001000000000000000
0.00000999999999995449
1.00E-06
0.00000099999999991773
0.00000100000000000000
0.00000100000000002876
1.00E-07
0.00000010000000005839
0.00000010000000000000
0.00000009999999994736
1.00E-08
0.00000000999999993923
0.00000001000000000000
0.00000001000000005025
1.00E-09
0.00000000100000008274
0.00000000100000000000
0.00000000099999997172
1.00E-10
0.00000000010000000827
0.00000000010000000000
0.00000000010000000827
1.00E-11
0.00000000001000000083
0.00000000001000000000
0.00000000001000000083
1.00E-12
0.00000000000100008890
0.00000000000100000000
0.00000000000099997788
1.00E-13
0.00000000000009992007
0.00000000000010000000
0.00000000000010003109
1.00E-14
0.00000000000000999201
0.00000000000001000000
0.00000000000000999201
1.00E-15
0.00000000000000000000
0.00000000000000100000
0.00000000000000000000
1.00E-16
0.00000000000000000000
0.00000000000000010000
0.00000000000000000000
1.00E-17
0.00000000000000000000
0.00000000000000001000
0.00000000000000000000
1.00E-18
0.00000000000000000000
0.00000000000000000100
0.00000000000000000000
1.00E-19
0.00000000000000000000
0.00000000000000000010
0.00000000000000000000
1.00E-20
0.00000000000000000000
0.00000000000000000001
0.00000000000000000000

() พึงสังเกตว่า ณ ตำแหน่งค่า e = 0.001 การเขียนแบบ 1 + e - 1 จะให้ผลลัพธ์ที่มีความคลาดเคลื่อนเกิดขึ้น ในขณะที่การเขียนแบบ 1 - (1 - e) จะเกิดปัญหาเดียวกันที่ค่า e = 0.0001
() ณ ตำแหน่งค่า e = 1E-15 การเขียนแบบ 1 + e - 1 และการเขียนแบบ 1 - (1 - e) จะให้ผลลัพธ์ที่เป็น 0 ในขณะที่การเขียนแบบ 1 - 1 + e ยังคงให้คำตอบที่ถูกต้องอยู่

ตัวอย่างที่ยกมานี้ทำการคำนวณโดยใช้ spread sheet สำหรับผู้ที่เขียนโปรแกรมแล้วให้ compiler แปลเป็นไฟล์ EXE แล้วนำไปคำนวณนั้นอาจได้ผลที่แตกต่างกันออกไปได้ เพราะแต่ก่อนก็เคยเจอเหมือนกันสำหรับ compiler ที่ฉลาดบางตัว เวลาที่เขียนคำสั่งทำนอง A = 1 + e - 1 หรือ A = 1 - 1 + e มันจะแปลงเป็น A = e ให้โดยอัตโนมัติ ดังนั้นไม่ว่าจะใช้ค่า e เป็นเท่าใดก็ได้ค่า A เท่ากับ e เสมอ 
  
(compiler ในที่นี้หมายถึงตัวแปลภาษา จากภาษาที่เราเขียนที่เป็นภาษาที่เราพอจะอ่านเข้าใจได้ ให้กลายเป็นภาษาที่คอมพิวเตอร์เข้าใจได้เพื่อมันจะได้ทำงานได้และสามารถสั่ง run ได้ เช่นเราเขียนโปรแกรมด้วยไฟล์ work แต่ตัว compiler จะแปลงเป็นไฟล์ .exe ให้)
 
แต่ก่อนนั้น ก่อนที่จะเริ่มทำ simulation สิ่งหนึ่งที่ต้องทำกันก็คือต้องหาว่าเครื่องคอมพิวเตอร์ที่ใช้นั้นมันเก็บเลขในส่วนที่เป็นเลขทศนิยมได้กี่ตำแหน่ง และส่วนที่เป็นเลขยกกำลังนั้นติดลบได้มากที่สุดเท่าใด จำนวนหลักหลังจุดทศนิยมที่มันเก็บได้เป็นตัวบ่งบอกถึงค่า "Machine precision" (ที่แปลเป็นไทยว่าค่าความเที่ยงของเครื่อง) หรือจำนวนที่น้อยที่สุดที่เมื่อบวกเข้ากับ 1 แล้วได้ผลลัพธ์ที่ไม่ใช่ 1 ส่วนเลขยกกำลังที่ติดลบได้มากที่สุดนั้นเป็นตัวบ่งบอกให้ถึงจำนวนที่น้อยที่สุดที่มากกว่าศูนย์ที่เครื่องนั้นสามารถเก็บค่า ค่าหลังนี้เรียกว่า "Machine accuracy" (ที่แปลเป็นไทยว่าค่าความแม่นของเครื่อง) อย่างเช่นในตัวอย่างที่ยกมาแสดงในตารางที่ ๑ นั้นค่า Machine precision ของเครื่องที่ผมใช้คำนวณจะอยู่ที่ประมาณ 10-16 หรือแปลได้ว่า แต่จำนวนที่น้อยที่สุดที่ไม่ใช่ศูนย์ที่เครื่องมันรับค่าได้จะอยู่ที่ประมาณ 10-307 (คือถ้าป้อนค่าน้อยกว่านี้มันจะบันทึกค่าเป็น 0) 
  
จากประสบการณ์ส่วนตัวที่เคยต้องเขียนโปรแกรม simulation ในยุคที่ต้องเขียน source code กันเอง การที่ต้องรู้ค่า Machine percision ของเครื่องคอมพิวเตอร์ที่ใช้คำนวณนั้นเป็นเรื่องสำคัญ (ส่วน Machine accuracy ไม่เคยใช้) เพราะมันส่งผลต่อการคำนวณค่าฟังก์ชันบางฟังก์ชันที่ไม่สามารถหาได้ด้วยเทคนิคการ analyse โดยตรง ตัวอย่างที่เห็นได้ชัดก็คือค่าอนุพันธ์ของฟังก์ชันที่ซับซ้อน การหาค่าอนุพันธ์ของฟังก์ชันเหล่านี้เราต้องกลับไปคำนวณโดยใช้สมการพื้นฐานคือ

ในทางทฤษฎีนั้น ค่าอนุพันธ์ของฟังก์ชัน ณ จุดใดก็เสมือนกับความชันของเส้นสัมผัสโค้ง ณ ตำแหน่งนั้น ความชันของเส้นสัมผัสโค้งประมาณได้จากผลต่างของค่า y และค่า x ที่ 2 ตำแหน่ง และเมื่อระยะห่างระหว่างจุด x 2 จุดนี้แคบลง (∆x วิ่งเข้าหา 0) ค่าความชันที่ได้ก็จะเป็นค่าความชันที่ถูกต้อง ณ ตำแหน่งนั้น 
  
ที่นี้เรามาลองคำนวณค่า df(x)/dx ของฟังก์ชัน f(x) = x2 ด้วยการใช้สมการพื้นฐานดูบ้าง โดยเทคนิคการ analyse นั้นฟังก์ชันนี้มีค่า df(x)/dx = 2x ดังนั้นที่ x = 1 จะได้ค่า df(x)/dx = 2 ผลการคำนวณด้วยสมการพื้นฐานที่ค่า ∆x ต่าง ๆ กันแสดงไว้ในตารางที่ ๒ ข้างล่าง (เครื่องนี้มีค่า Machine precision อยู่ที่ระดับ 10-16)

ตารางที่ ๒ ผลการคำนวณค่า df(x)/dx ของฟังก์ชัน y = x2 ด้วยการใช้สมการพื้นฐานที่ค่า ∆x ต่าง ๆ กัน

dx
f(x+dx)
f(x)
df/dx (numerical)
1.00E-01
1.2100000000000000
1.0000000000000000
2.1000000000000000
1.00E-02
1.0201000000000000
1.0000000000000000
2.0100000000000000
1.00E-03
1.0020010000000000
1.0000000000000000
2.0009999999997000
1.00E-04
1.0002000100000000
1.0000000000000000
2.0000999999991700
1.00E-05
1.0000200001000000
1.0000000000000000
2.0000100000139300
1.00E-06
1.0000020000010000
1.0000000000000000
2.0000009999243700
1.00E-07
1.0000002000000100
1.0000000000000000
2.0000001010878100
1.00E-08
1.0000000200000000
1.0000000000000000
1.9999999878450600
1.00E-09
1.0000000020000000
1.0000000000000000
2.0000001654807400
1.00E-10
1.0000000002000000
1.0000000000000000
2.0000001654807400
1.00E-11
1.0000000000200000
1.0000000000000000
2.0000001654807400
1.00E-12
1.0000000000020000
1.0000000000000000
2.0001778011646800
1.00E-13
1.0000000000002000
1.0000000000000000
1.9984014443252800
1.00E-14
1.0000000000000200
1.0000000000000000
1.9984014443252800
1.00E-15
1.0000000000000000
1.0000000000000000
0.0000000000000000



จะเห็นว่าเมื่อลดค่า ∆x ลงเรื่อย ๆ ค่า df(x)/dx ที่คำนวณได้จะเข้าหาค่าที่ถูกต้องมากขึ้น แต่เมื่อ ∆x ลดลงถึงระดับหนึ่งกลับพบว่าค่าที่คำนวณได้นั้นจะผิดเพี้ยนมากขึ้น และมากขึ้นตามค่า ∆x ที่ลดต่ำลง และพอถึงระดับหนึ่งจะพบว่าได้ค่าอนุพันธ์เป็นศูนย์ ซึ่งเป็นค่าที่ผิด สาเหตุเป็นเพราะที่ระดับนี้ค่า x + ∆x จะมีค่าเท่ากับ x ทำให้ f(x + ∆x) = f(x)
 
จุดที่ให้คำตอบที่ถูกต้องมากที่สุดอยู่ที่ประมาณรากที่สองของค่า Machine precision กล่าวคือถ้าเครื่องคอมพิวเตอร์ที่ใช้นั้นคำนวณด้วยความละเอียดขนาด 16 ตำแหน่งหรือค่า Machine precision อยู่ที่ระดับ 10-16 ถ้า x มีค่าอยู่ที่ระดับ 1 ค่า ∆x ที่ให้ผลการคำนวณที่ถูกต้องที่สุดก็อยู่ที่ประมาณ 10-8 แต่ต้องย้ำนิดนึงว่าค่านี้เป็นค่าสัมพัทธ์ กล่าวคือถ้า x มีค่าอยู่ที่ระดับ 103 ค่า ∆x ที่ให้ผลการคำนวณที่ถูกต้องที่สุดก็อยู่ที่ประมาณ 10-5 และถ้า x มีค่าอยู่ที่ระดับ 10-3 ค่า ∆x ที่ให้ผลการคำนวณที่ถูกต้องที่สุดก็อยู่ที่ประมาณ 10-11 หรือพูดง่าย ๆ ก็คือค่า x มีค่าเท่าใด ก็เอาค่า 10-8 (ซึ่งเป็นค่ารากที่สองของค่า Machine precision ของเครื่องที่ผมใช้คำนวณ) คูณเข้าไป

วันจันทร์ที่ 26 พฤศจิกายน พ.ศ. 2561

เมื่อ 1 ไม่เท่ากับ 0.1 x 10 (การทำวิทยานิพนธ์ภาคปฏิบัติ ตอนที่ ๙๓) MO Memoir : Monday 26 November 2561

ผมได้สัมผัสกับคอมพิวเตอร์ครั้งแรกก็ตอนเรียนแลปฟิสิกส์ปี ๑ เมื่อปี ๒๕๒๗ เป็นเครื่องไมโครคอมพิวเตอร์ 8 บิท ได้เรียนภาษา BASIC แบบไม่รู้เรื่องรู้ราวอะไรเลย (อันที่จริงยังไม่รู้ด้วยซ้ำว่าคอมพิวเตอร์คืออะไร) จากนั้นในเทอมปลายจึงได้เรียนเขียนภาษา FORTRAN ตัวที่เรียกว่า WATFIVที่เป็นตัวที่มหาวิทยาลัย Waterloo พัฒนาขึ้นมา ตัวนี้เป็นตัวก้ำกึ่งระหว่าง FORTRAN IV (ตัวนี้มาก่อน FORTRAN 66 อีก) และ FORTRAN 77 (มาตรฐานปีค.ศ. ๑๙๗๗) ปีนั้นเรียกว่าโชคดีกว่ารุ่นพี่ตรงที่ เป็นปีแรกที่สามารถพิมพ์โปรแกรมผ่านทางเทอร์มินอลได้แล้ว ไม่ต้องใช้บัตรเจาะรู (บัตรกระดาษแข็ง ๑ บัตรต่อ ๑ บรรทัด ที่ต้องเจาะรูแล้วไปให้เครื่องอ่านบัตรอ่านอีกที)
 
พอถึงปี ๔ ก่อนจบต้องเรียนวิชา Plant Design (ออกแบบโรงงานนะ ไม่ใช่ออกแบบพืช) กัน ตอนนั้นมี IBM PC มาให้ใช้งานกันแล้ว แต่ยังต้องนั่งเขียนโปรแกรมภาษา BASIC เพื่อทำการคำนวณทุกอย่าง (ไม่มีซอร์ฟแวร์สำเร็จรูปให้ใช้เหมือนทุกวันนี้) ระหว่างการคำนวณก็ได้เห็นพฤติกรรมแปลก ๆ ของผลการคำนวณ เช่นถ้าให้คำนวณ n = n + 1 โดยให้ n เริ่มต้นจาก 0 ก็พบว่าพอคำนวณไปได้สักพักคำตอบที่ได้มันกลับไม่ได้เป็นเลขจำนวนเต็ม มันดันมีจุดทศนิยมโผล่ออกมา แต่ตอนนั้นก็ไม่รู้ว่ามันเกิดจากอะไร และไม่รู้ด้วยว่าจะไปหาคำอธิบายจากใครได้
 
ตอนเรียนเขียน FORTRAN นั้นก็เรียนรู้กันว่า สำหรับบางคำสั่ง (เช่นคำสั่งนับการวน loop) ต้องมีการกำหนดค่าตัวแปรให้เป็นชนิด "จำนวนเต็ม" หรือ "Integer" ห้ามกำหนดเป็น "จำนวนจริง" หรือ "Real" ซึ่งก็ไม่รู้เหมือนกันว่าทำไมต้องทำอย่างนั้น และยังไม่รู้ด้วยว่าคำสั่ง "Double precision" นั้นมีเอาไว้ทำไม
 
ข้อสงสัยต่าง ๆ สมัยเรียนนั้นคงจะลืมไปหมด ถ้าไม่ใช่เป็นเพราะตอนไปเรียนโทที่ต่างประเทศได้ทำงานวิจัยที่ต้องเขียนภาษา FORTRAN แล้วอาจารย์ที่ปรึกษาแนะนำให้ไปลงเรียนวิชาพื้นฐานการคำนวณเชิงตัวเลข (การแก้โจทย์สมการคณิตศาสตร์ด้วยวิธีการคำนวณเชิงตัวเลข) ตอนนั้นเองถึงได้เข้าใจว่าบนคอมพิวเตอร์นั้น "จำนวนเต็ม" ต่างกับ "จำนวนจริง" อย่างไร และมันเกิดอะไรขึ้นเมื่อเราเอาเลขฐาน 10 (ซึ่งเป็นตัวเลขที่เราใช้กันในชีวิตประจำวัน) ไปให้คอมพิวเตอร์คำนวณด้วยระบบเลขฐาน 2 ที่มันเข้าใจ
 
เนื้อหาในบทความชุดนี้เป็นการนำเอาสิ่งที่เขียนไว้สำหรับสอนนิสิตวิศวกรรมเคมีปี ๓ ในเรื่องพื้นฐานการคำนวณเชิงตัวเลขใน "บทที่ ๑ การคำนวณในระบบเลขทศนิยม" ซึ่งอันที่จริงก็มีไฟล์ pdf ไว้ให้ดาวน์โหลดฟรีบนหน้า blog อยู่แล้ว เพียงแต่รูปแบบไฟล์ pdf คงทำให้การค้นหาโดยใช้คำสำคัญค้นหาด้วย google นั้นทำให้มันหาไม่เจอ ก็เลยนำมาเขียนเป็น text ไว้บนหน้า blog หน่อย เพราะช่วงสัปดาห์ที่ผ่านมาเห็นมีการกดแชร์บางบทความกัน เลยทำให้รู้ว่าเรื่อง "ความรู้พื้นฐาน" นี้ยังมีคนไม่รู้อยู่มาก แม้ว่าจะเป็นงานในสายวิชาชีพตัวเอง โดยในบทความชุดนี้จะขอเริ่มจากคำถามง่าย ๆ ข้างล่างก่อน

คำถาม ถ้าต้องการคำนวณค่า (1 + 1) บนคอมพิวเตอร์ โดยคำนวณในรูปแบบ
) จำนวนเต็ม ค่าของตัวเลขแต่ละตัวจะถูกบันทึกไว้ในรูปแบบ 1
ดังนั้นค่าที่ได้จากการคำนวณ (1 + 1) = ...............
) จำนวนจริง ค่าของตัวเลขแต่ละตัวจะถูกบันทึกไว้ในรูปแบบ 0.1 x 10
ดังนั้นค่าที่ได้จากการคำนวณ (0.1 x 10 + 0.1 x 10) = ..............
เมื่อทำการคำนวณ ผลลัพธ์ที่ได้จะออกมาเหมือนกันหรือไม่
ในชีวิตประจำวันของเรานั้นเราใช้เลขฐาน 10 (decimal digit) เป็นหลักในการติดต่อสื่อสารและการคำนวณต่าง ๆ ตัวเลขฐาน 10 ที่ใช้มีตั้งแต่ 0 ถึง 9 ค่าของเลขฐาน 10 แต่ละจำนวนขึ้นอยู่กับค่าประจำหลักดังแสดงในตารางที่ ๑ 
 


ตารางที่ ค่าประจำหลักของเลขฐาน 10

หลักที่
5
4
3
2
1
จุดทศนิยม
1
2
3
4
5
ค่าประจำหลัก
104
103
102
101
100
.
10-1
10-2
10-3
10-4
10-5



ระบบเลขฐาน 2 (binary digit) เป็นระบบการคำนวณที่ใช้กันในเครื่องคอมพิวเตอร์แบบดิจิตอล (digital computer) ทั่วไป เลขที่ใช้จะมีเพียงแค่ 0 กับ 1 ค่าของเลขฐาน 2 แต่ละจำนวนขึ้นอยู่กับค่าประจำหลักดังแสดงในตารางที่ ๒ ข้างล่าง



ตารางที่ ๒ ค่าประจำหลักของเลขฐาน 2

หลักที่
5
4
3
2
1
จุดทศนิยม
1
2
3
4
5
ค่าประจำหลัก
24
23
22
21
20
.
2-1
2-2
2-3
2-4
2-5



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



ตารางที่ ๓ ตัวอย่างตัวเลขฐาน 10 ที่เป็นจำนวนเต็มและตัวเลขฐาน 2 ที่เทียบเท่ากัน


เลขฐาน 10
เลขฐานสอง
18
10010
100
1100100
10000
10011100010000
152431
100101101101101111
10000000
100110001001011010000011



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

ต่อไปขอให้พิจารณากรณีการเปลี่ยนเลขฐานสิบที่เป็นเลขทศนิยมให้เป็นเลขฐานสองดังแสดงในตารางที่ ๔



ตารางที่ ๔ ตัวอย่างตัวเลขฐาน 10 ที่เป็นเลขทศนิยมและตัวเลขฐาน 2 ที่เทียบเท่ากันหรือใกล้เคียงกัน


เลขฐาน 10
เลขฐานสอง
0.1
0.0001100110011.....
0.3
0.0100110011001.....
0.5
0.1
0.75
0.11
0.93359375
0.11111111

ตัวอย่างในตารางที่ ๔ แสดงให้เห็นว่ามีเลขทศนิยมในระบบเลขฐาน 10 บางจำนวนเท่านั้นที่สามารถหาเลขทศนิยมในระบบเลขฐาน 2 ที่มีค่าเท่ากันได้
 
ตัวเลขเช่น 0.5, 0.25, 0.125, 0.0625 ฯลฯ เป็นตัวเลขทศนิยมในระบบเลขฐาน 10 ที่สามารถแทนด้วยค่าตัวเลขทศนิยมในระบบเลขฐานสองที่มีค่าเท่ากันได้ ส่วนค่า 0.1 ในระบบเลขฐาน 10 จะมีค่าเป็นทศนิยมไม่รู้จบในระบบเลขฐาน 2 ดังนั้นเมื่อทำการป้อนข้อมูลที่มีค่าเท่ากับ 0.1 ในระบบเลขฐาน10 เข้าไปในคอมพิวเตอร์ คอมพิวเตอร์จะทำการแทนค่าด้วยเลขฐาน 2 ที่มีค่า "ใกล้เคียง" กับเลข 0.1 ในระบบเลขฐาน 10 กล่าวอีกนัยหนึ่งคือเรามีความคลาดเคลื่อน (error) เกิดขึ้นโดยที่เรายังไม่ได้เริ่มทำการคำนวณเลย
 
เวลาที่เราสั่งให้คอมพิวเตอร์เก็บค่าตัวเลข (ที่ไม่มีทศนิยม) ในรูป "จำนวนเต็ม" นั้น เช่นเลข 100 คอมพิวเตอร์ก็จะแปลงเป็นเลขฐาน 2 คือ 1100100 ซึ่งเป็นจำนวนที่เทียบเท่ากับพอดี แต่ถ้าเราสั่งให้มันเก็บค่า 100 ในรูปแบบ "จำนวนจริง" มันก็จะเก็บค่าในรูปแบบทำนอง 0.1 x 102 (อันที่จริงก็ไม่ใช่แบบนี้ เพียงแต่ต้องการเขียนให้เห็นภาพหน่อย) ซึ่งถ้าจะว่ากันทางคณิตศาสตร์แล้วมันก็ควรเท่ากับ 100 แต่ปัญหาก็คือเลข 0.1 ในระบบฐาน 10 นั้น มันไม่มีเลขในระบบฐาน 2 ที่ "เท่ากันพอดี" สิ่งที่เครื่องมันทำให้ก็คือมันจะหาเลขฐาน 2 ที่ใกล้เคียงที่สุดให้ ส่วนจะใกล้ได้มากแค่ไหนนั้นก็ขึ้นอยู่กับว่าเราใช้จำนวนบิท (bit) มากน้อยเท่าใดในการเก็บข้อมูล ในทำนองเดียวกันถ้าเราให้มันเก็บค่าตัวเลข 0.001 ในระบบฐาน 10 มันก็จะแปลงค่าเป็น 0.1 x 10-2 ซึ่งก็จะมีปัญหาว่ามันจะหาค่าในระบบฐาน 2 ที่ใกล้เคียงที่สุดให้
 
ผลที่ตามมาก็คือ เรายังไม่ได้เริ่มทำการคำนวณเลย เราก็มีความคลาดเคลื่อนในการคำนวณแล้ว
 
ผลของความคลาดเคลื่อนตรงนี้ทดลองเองได้ไม่ยากครับ ลองเปิดโปรแกรม spread sheet ขึ้นมา แล้วลองป้อนเลข 0.1 เรียงแถวลงไปในคอลัมน์ด้านซ้าย (เขียนเยอะหลายสิบตัวหน่อย) จากนั้นก็ลองใช้คำสั่ง sum ดูทุก ๆ 10 จำนวนดูก็ได้ครับ แล้วดูว่าค่าผลลัพธ์ที่ได้นั้นเป็นอย่างไร



ตารางที่ ๕ ผลรวมของค่า 0.1 (คำนวณด้วยโปรแกรม spread sheet OpenOffice 4.1.5)

1
1.000000000000000E-01

2
1.000000000000000E-01

---
---

10
1.000000000000000E-01
1.000000000000000E+00
11
1.000000000000000E-01

---
---

20
1.000000000000000E-01
2.000000000000000E+00
21
1.000000000000000E-01

---
---

30
1.000000000000000E-01
3.000000000000000E+00
31
1.000000000000000E-01

---
---

40
1.000000000000000E-01
4.000000000000000E+00
41
1.000000000000000E-01

---
---

50
1.000000000000000E-01
5.000000000000000E+00
51
1.000000000000000E-01

---
---

60
1.000000000000000E-01
6.000000000000000E+00
61
1.000000000000000E-01

---
---

70
1.000000000000000E-01
6.999999999999990E+00
71
1.000000000000000E-01

---
---

80
1.000000000000000E-01
7.999999999999990E+00


ผลการคำนวณที่แสดงในตารางที่ ๕ อันที่จริงเคยทำไว้ตั้งแต่สมัยใช้คอม 16 บิท พอเปลี่ยนเป็น 32 บิทหรือ 64 บิทก็ยังพบปัญหาแบบเดียวกันอยู่ เพียงแต่พบช้าลงเท่านั้นเอง ตอนที่เอา 0.1 จำนวน 10 20 ไปจนถึง 60 ตัวมารวมกัน ผลลรวมออกมาก็เป็น 1 2 3 4 5 และ 6 ดังที่เราคิดว่ามันควรเป็น แต่พอเอา 0.1 จำนวน 70 ตัวมาบวกเข้าด้วยกัน ปรากฏว่าไม่ได้ 7 กลับได้ 6.999999.... และหลังจากนั้นก็ได้ผลที่น้อยกว่าค่าที่ควรจะเป็นเสมอ ส่วนที่ว่าจะเห็นปรากฏการณ์นี้เมื่อใดนั้นขึ้นอยู่กับว่าคุณใช้กี่บิทในการเก็บข้อมูล ถ้าใช้จำนวนบิทมากก็จะเห็นมันเกิดช้าหน่อย แต่ก็ยังเกิดอยู่ดี 
  
ส่วนที่ว่าเครื่องนั้นให้ความละเอียดมากน้อยแค่ไหน ก็ลองเพิ่มจำนวนจุดทศนิยมให้มันเขียนรายงานผล ถ้าพบว่าเมื่อใดที่เพิ่มแล้วตำแหน่งท้ายสุดนั้นมันมีค่าเป็น 0 ตลอด นั่นก็แสดงว่าจุดนั้นมันเกินความละเอียดของเครื่องแล้ว อย่างในตารางที่ ๕ ก็อยู่ที่ 15 ตำแหน่ง
 
ตารางที่ ๖ เป็นอีกตัวอย่างหนึ่งที่จะไปทดลองเล่นดูเองกับโปรแกรม spread sheetก็ได้ครับ ตัวเลข 0.3333... ในคอลัมน์ข้างซ้ายผมคำนวณด้วยคำสั่ง 1/3 จากนั้นก็นำมาบวกรวมกัน ส่วนคอลัมน์ข้างขวานั้นผมป้อนตัวเลข 0.3333... ลงไปโดยตรงโดยให้ตัวเลขที่ "แสดงผลบนหน้าจอ" เหมือนกับตัวเลขในคอลัมน์ด้านซ้าย จากนั้นก็นำมาบวกรวมกัน จะเห็นว่าผลลัพธ์ที่ได้นั้นแตกต่างกันอยู่เล็กน้อยที่ทศนิยมตำแหน่งสุดท้าย (ที่ไฮไลต์ตัวหนาสีแดงเอาไว้) นั่นแสดงว่าตัวเลขที่เครื่องแสดงผลออกมาทางหน้าจอ แม้ว่ามันจะเหมือนกัน แต่สิ่งที่อยู่ในหน่วยความจำของมันนั้นมันแตกต่างกันอยู่



ตารางที่ ๖ ผลรวมของตัวเลขจำนวน 3 จำนวนที่ "เหมือนกัน"



คำนวณจาก 1/3

พิมพ์ตัวเลขลงไปโดยตรง

0.333333333333333000

0.333333333333333000

0.333333333333333000

0.333333333333333000
รวม
0.666666666666667000

0.666666666666666000



ผลจากการที่เครื่องไม่สามารถหาเลขฐาน 2 ที่เทียบเท่ากับเลขทศนิยมในระบบฐาน 10 ได้ทุกจำนวน ที่ทำให้ผลการคำนวณที่ได้นั้นมีความผิดเพี้ยนไป จะเห็นได้ชัดเจนและรวดเร็วแค่ไหนนั้นขึ้นอยู่กับว่าเราใช้กี่บิทในการเก็บข้อมูล ยิ่งใช้บิทมากก็ยิ่งช่วยบรรเทาผลกระทบนี้ แต่ใช้ว่ามันจะหายไป มันก็ยังมีอยู่ แต่อาจเห็นได้ไม่ชัดเจนนัก เว้นแต่จะมีจำนวนครั้งการคำนวณที่มาก (ดังนั้นถ้านำสิ่งที่แนะนำนี้ไปทดสอบด้วยเครื่องคอมพิวเตอร์ต่างระบบกัน ก็อาจให้ผลที่แตกต่างกันไปได้) ด้วยเหตุนี้บางโปรแกรมจึงมีคำสั่งให้เพิ่มจำนวนบิทเก็บข้อมูลขึ้นเป็น 2 เท่า (เช่นคำสั่ง Double precision ในภาษา FORTRAN ที่ผมเรียนมา) แต่นั่นก็หมายถึงหน่วยความจำที่ต้องการมากขึ้นและเวลาที่ต้องใช้ในการคำนวณที่เพิ่มมากขึ้นตามไปด้วย