ดาวน์โหลดโปรแกรม RSS Reader ได้ที่นี่ ...

|
|
|
Visitors - Session views |       
7 ธันวาคม พ.ศ.2549 39 Users On-Line. |
|
Visitors - Page views |        1 กุมภาพันธ์ พ.ศ.2551 |
|
|
|
 |
|
แจกฟรีโค้ดฟังค์ชั่นในการปัดเศษเลขทศนิยมให้เป็นจำนวนเต็ม - VB6 |
Category »
VB 6/VB.Net โดย : Webmaster เมื่อ 5/2/2554 เวลา: 19:58 | (อ่าน : 46789) |
ข้้อมูลคอมพิวเตอร์มันไม่ได้อยู่ในรูปแบบของเลขฐาน 10 แต่มันจะอยู่ในลักษณะของ Binary หรือ เลขฐาน 2 ซึ่งปกติแล้วเรามักจะใช้ 0 หรือ 1 มาอธิบายกัน (จริงๆแล้วมันอาศัยระดับความต่างศักย์ไฟฟ้า หรือ ความเข้มของสนามแม่เหล็ก เป็นต้น) ... ซึ่งตอนที่ผมเรียนวิชา Numerical Method หรือ วิีธีการเชิงตัวเลข ก็ยิ่งทำให้เข้าใจลักษณะของการจัดเก็บข้อมูลได้มากขึ้น ตัวอย่างง่ายๆเช่น ตัวเลข 1.50 พอมันไปอยู่ในคอมพิวเตอร์มันอาจจะเป็น 1.4999999999 ก็ได้ แต่ครั้นพอนำมาแสดงผล มันจะเกิดการปัดเศษ (Rounding) ทำให้ได้ค่าเท่ากับ 1.50 แทนยังไงล่ะครับ (โดยใช้ Format(Value, "0.00") เข้าช่วย) ... มาว่ากันถึงเรื่องตัวแปลภาษา ซึ่งแต่ละตัวก็จะใช้ Algorithm ที่แตกต่างกันไปในการปัดเศษ ... สำหรับใน Visual Basic จะใช้ Banker's Rounding (อ่านรายละเอียดเพิ่มเติมที่นี่) คือ หากต่ำกว่า 4 ปัดเศษทิ้ง หากสูงกว่า 6 ก็ให้ปัดขึ้น แต่มันเกิดปัญหากับค่า 5 นี่แหละจะให้มันปัดขึ้น หรือ ปัดทิ้งดีล่ะ ... แอ่น แอ้น
Banker's Rounding มันมีหลักการคิดปัดเลขทศนิยมดังนี้ คือ
- ถ้าตัวเลขข้างหน้า 5 หากเป็นเลขคี่ ก็ให้ปัดขึ้น หากไม่ใช่ก็ให้ปัดเศษทิ้ง
ตัวอย่าง ...
- Visual Basic 6 ใช้ Round Function
เป็นฟังค์ชั่น (หรือคำสั่งภายใน) ที่จะคืนค่าการปัดเศษตามจำนวนตำแหน่งจุดทศนิยมที่ต้องการ Round(expression [,numdecimalplaces]) ? Round(10.15, 1) 10.2 <-- หน้าเลข 5 เป็น 1 (เลขคี่) ปัดขึ้น ? Round(10.25, 1) 10.2 <-- หน้าเลข 5 เป็น 2 (เลขคู่) ปัดเศษทิ้ง ? Round(10.35, 1) 10.4 <-- หน้าเลข 5 เป็น 3 (เลขคี่) ปัดขึ้น ? Round(10.45, 1) 10.4 <-- หน้าเลข 5 เป็น 4 (เลขคู่) ปัดเศษทิ้ง
- แล้วมันใช่หรือเปล่า ...
? Round(134.425, 2) 134.43 <-- ผิดมั้ยล่ะครับ ... ก็ข้างหน้า 5 มันเป็นเลขคู่ แล้วทำไมมันปัดเศษขึ้น ? Round (1.425, 2) 1.42 <-- แบบนี้ถูก
- ลองดูการแก้ปัญหา
สำหรับ VB6 ? Round(CDec(134.425), 2) 134.42 สำหรับ VB.Net ? Math.Round(CDec(134.425), 2) 134.42D ? Math.Round(Decimal.Parse(134.425), 2) 134.42D
ปัญหาต่อมา ...
- การปัดเศษเพื่อให้เป็นเลขจำนวนเต็ม
? Round(12.5, 0) 12 <--ทำไมมันไม่ปัดขึ้น ความเป็นจริงแล้ว มันควรจะเป็น 13 ไม่ใช่ 12 ซิ ... (คงจะคิดกันว่าข้างหน้าเลข 5 มันเป็นเลขคู่ ชิมิๆๆ) งั้นเราลองมาดูแบบนี้ ? Round(11.5, 0) 12 <-- มันปัดขึ้น ถามว่า ... คุณเห็นดีแล้วหรือทองก้อนเจ้าขา ที่คุณนำเมียน้อยมา 55555+ ฟังเพลงน้ำตาเมียหลวง - ผ่องศรี วรนุช ... ชอบจริงๆเลยเพลงนี้ครับพี่น้อง ลองมาสมมุติตัวเลขเล่นๆกัน ... โดยการเขียนโปรแกรมเพื่อตัดเกรดแบบอิงกลุ่ม คะแนน 54 - 58 ได้เกรด C คะแนน 59 - 62 ได้เกรด C+ คนที่ได้ 53.5 ก็จะเลื่อนขึ้นมาที่เกรด C แต่คนที่ได้ 58.5 กลับไม่ได้เลื่อนเกรดไปที่ C+ ... หากประมวลผลออกมาแบบนี้ มีหวังนักศึกษาด่า (ลับหลัง) เละแหละครับ เพราะมันเกิดความไม่เป็นธรรมนี่นา
- ลองดูการแก้ปัญหา
? Format(12.5, "0") 13 ? Round(12.5 + 0.000000000000005, 0) <-- ตัวนี้ 5E-15 ครับ หรือลอง Round(12.5 + 5E-16, 0) ดูครับ 13
บางคนอาจจะคิดว่านี่เป็น Bug ของ VB6 (ฮาตรึมล่ะ) VB.Net ก็เป็นเหมือนกันครับ ลองใช้ Math.Round() ได้เลย แล้วมันเป็นที่คอมพิวเตอร์ หรือ ตัวแปลภาษา กันล่ะเนี่ย หากสนใจใคร่รู้ ... คลิ๊กอ่านได้ที่นี่ครับ หมายเหตุ: ที่บอกว่าผลลัพธ์ VB6 กับ VB.Net มันเหมือนกัน หมายความว่าใช้คำสั่ง Round แต่เพียงอย่างเดียวเท่านั้นน่ะครับ
ผมมีโค้ดเล็กๆให้ดูเรื่องของชนิดความเที่ยงตรงของข้อมูล (Single/Double Precision)
- Dim X As Double
Dim i As Integer
X = 0 For i = 1 To 17 X = X + 10 / 17 Next i
If X = 10 Then MsgBox X & " = 10" Else MsgBox X & " <> 10" End If
- ผลที่ได้
? X 10 <-- ตอนที่ Debug ออกมาดู บรรทัด If X = 10 Then
 การ Debug เพื่อดูผลจริง
 งงน่ะจังงัง ... แบบนี้คอมพิวเตอร์สับสนเลย เอิ๊กๆๆๆๆ ลองปรับรูปแบบให้ X = Format(X, "0") หรือลองเปลี่ยน X เป็น Single ดูว่าผลที่ได้มันจะเป็นอย่างไร
- คราวนี้มาดู Visual Basic 2008 กันบ้าง
 ดูจาก VB.Net จะเห็นผลได้อย่างชัดเจนเลยว่า ในหน่วยความจำไม่ได้เก็บค่า 10 แบบจำนวนเต็มเอาไว้ เพราะผลลัพธ์ที่ได้มันมาจากการปัดเศษ
 สำหรับ VB 2008 ... ก็เหมือนกับ VB6 (จับผิดตระกูล Dot Net ได้ คิกๆๆๆๆ) เฉลยให้เลยดีกว่า ... การแก้ปัญหา If Round(X, 1) = 10 Then ... หรือ If Format(X, "0") = 10 Then แบบนี้เขาเรียกว่าการเกิด Round Off Error หรือ ความผิดพลาดจากการปัดเศษ
- ตัวอย่างของการจัดเก็บข้อมูลของคอมพิวเตอร์ ... และควรไปศึกษาเพิ่มเติม
 รูปแบบการจัดเก็บข้อมูลตัวเลขทางวิทยาศาสตร์ (ก็ตัวเลขที่มีจุดทศนิยมนั่นแหละครับ) ชนิดความเที่ยงตรงเชิงเดี่ยว (Single Precision) ขนาด 32 Bit คำอธิบายแบบย่อ: บิตซ้ายมือสุด คือ บิตเครื่องหมาย (Signed) หากเป็น 0 คือเลขบวก, หากเป็น 1 คือเลขลบ 8 บิตถัดมาเรียกว่า Exponent คือ เลขยกกำลัง แบ่งออกเป็น
- หากไม่คิดเครื่องหมาย หรือ Unsigned (ค่า 0 กับจำนวนเต็มบวก) มีค่าเลขยกกำลังได้ระหว่าง 0 - 255
- หากคิดเครื่องหมาย หรือ Signed (-, 0 และ +) มีค่าเลขยกกำลังได้ระหว่าง -128 ถึง +127 (อ่านรายละเอียดเพิ่มเติมที่นี่)
23 บิตสุดท้าย (Mantissa) คือส่วนเก็บเลขจุดทศนิยม แต่อยู่ในรูปแบบ Binary น่ะครับ สรุปสุดท้าย 1 Kg. = 1000 g. (เราคิดแบบฐาน 10) ... แต่สำหรับเลข Binary ขนาด 1 KByte. = 1024 Byte. (เราคิดแบบฐาน 2) ดังนั้นข้อมูลที่จัดเก็บในคอมพิวเตอร์ จะได้กราฟการเพิ่ม-ลดค่า ออกมาไม่เป็นสมการแบบเชิงเส้น (Non Linear Equation)
|
 |
มาเขียนฟังค์ชั่นใช้งานเองดีกว่าครับ ... พี่น้อง
แนวคิดฟังค์ชั่นของการปัดเศษเลขทศนิยมให้เป็นเลขจำนวนเต็ม
- มีค่าชุดตัวเลขแบบทศนิยม เช่น 12.95 กับค่าที่ใช้เปรียบเทียบในการปัดเศษ เช่น 0.75
- แยกชุดตัวเลขจำนวนเต็ม และ เลขทศนิยมออกจากกัน เช่น 12.95 ก็จะได้ 12 กับ 0.95
- เอาชุดเลขทศนิยมที่ได้ (0.95) มาเปรียบเทียบกับค่าเลขทศนิยมที่ต้องการปัดเศษ (0.75)
ทำการทดสอบค่าเลขทศนิยม กับค่าที่ต้องการจะปัดเศษ เช่น 0.95 >= 0.75
- หากมีค่ามากกว่า หรือ เท่ากับ ก็ให้เพิ่มเลขจำนวนเต็มขึ้น 1
- หากมีค่าน้อยกว่า ก็คืนค่าเลขจำนวนเต็มเดิมกลับคืนไป
- คำเตือน ... โจทย์ข้อนี้คนละเรื่องกับคำสั่ง Int() หรือ Fix() น่ะครับ อิอิอิอิอิ
|
มาดูโค้ดกันเถอะ ...
Option Explicit
' #####################################################
' โปรแกรมย่อยแบบฟังค์ชั่น (สามารถคืนค่ากลับมาได้) เพื่อใช้ในการปัดเศษเลขทศนิยม
' ให้เป็นเลขจำนวนเต็ม มีการรับค่าเข้ามา 2 ค่าคือ
' NumberToRound ชุดตัวเลขที่ใช้ทดสอบในการปัดเศษ
' DoubleToRound (เป็น Optional) คือจะส่งค่ามาก็ได้ หรือไม่ส่งมาก็ได้ เพื่อใช้เปรียบเทียบการปัดเศษ
' แต่ถ้าหากไม่ส่งค่ามา ตัวแปรตัวนี้จะมีค่าจะเท่ากับ 0 ไปโดยปริยาย
' และมีการคืนค่ากลับเป็นแบบเลขจำนวนเต็ม Long
' #####################################################
Public Function RoundNumber( _
ByVal NumberToRound As Double, _
Optional ByVal DoubleToRound As Double = 0 _
) As Long
' ประกาศตัวแปรแบบ Array เพื่อแยกส่วนของเลขจำนวนเต็ม และ เลขทศนิยมออกจากกัน
Dim Parts() As String
' ตรวจสอบก่อนว่าชุดตัวเลขที่ส่งมานี้เป็นเลขทศนิยมหรือไม่ หากไม่ใช่ให้เด้งออกไปจากโปรแกรมย่อยทันที
' โดยใช้ฟังค์ชั่น (หรือคำสั่ง) InStr ทดสอบหาเครื่องหมายจุดทศนิยม
' เริ่มต้นจากตำแหน่งที่ 1 ตามจำนวนตัวเลขของตัวแปร NumberToRound ที่ส่งมา
' หากหาไม่พบฟังค์ชั่นนี้จะคืนค่า 0 กลับมา แต่ถ้าหากพบก็จะคืนค่าตำแหน่งที่เจอจุดทศนิยมกลับมา
If InStr(1, NumberToRound, ".") = 0 Then
' หากเป็นเลขจำนวนเต็ม (ไม่มีจุดทศนิยม) ก็ให้กลับออกจากโปรแกรมย่อย
RoundNumber = NumberToRound
Else
' เริ่มต้นการแยกชุดตัวเลขจำนวนเต็ม และ เลขทศนิยมออกจากกัน
' โดยใช้คำสั่ง Split(ชุดข้อความ, เครื่องหมายที่ใช้แยก)
' โดยที่ Parts(0) จะเก็บเลขจำนวนเต็มเอาไว้
' โดยที่ Parts(1) จะเก็บเลขทศนิยม
Parts = Split(NumberToRound, ".")
' ทดสอบว่าชุดเลขทศนิยมมีค่ามากกว่าหรือเท่ากับ จำนวนเลขทศนิยมที่ต้องการปัดเศษให้เป็นเลขจำนวนเต็ม
' ใส่เครื่องหมายจุดทศนิยมนำหน้าก่อน จากนั้นใช้ CDbl เพื่อแปลงให้กลายเป็นเลขทศนิยม
' หากค่าเลขทศนิยมตัวหลักมากกว่า หรือเท่ากับค่าที่กำหนด เช่น 0.5 >= 0.5
' โจทย์ให้คิด: ต้องตรวจสอบค่า DoubleToRound ก่อนว่ามันน้อยกว่า 1 หรือไม่ ?
If CDbl("0." & Parts(1)) >= DoubleToRound Then
' ให้เพิ่มค่าเลขจำนวนเต็มใน Parts(0) ขึ้นอีก 1 คือคำตอบสุดท้าย ... อิอิอิอิอิ
RoundNumber = Parts(0) + 1
Else
' หากไม่ใช่ให้คืนเลขจำนวนเต็มตัวเดิมใน Parts(0) กลับคืนไป
RoundNumber = Parts(0)
End If
End If
End Function
การนำไปใช้งาน
Call RoundNumber(12.5)
Debug.Print RoundNumber(12.75, 0.75)
หรือจากหน้าต่าง Immediate Windows
? RoundNumber(12.5)
13
? RoundNumber(12.5, 0)
13
? RoundNumber(12.4, 0.5)
12
? RoundNumber(12.5, 0.5)
13
|
ฝากโจทย์อีกตัวให้คิดเป็นการบ้าน ... คำถามมีอยู่ว่าจะเขียนโปรแกรมอย่างไร โดยมีเงื่อนไขดังต่อไปนี้
หากเศษสตางค์อยู่ระหว่าง 1 - 25 สตางค์ ให้แสดงผลเป็น 25 สตางค์
หากเศษสตางค์อยู่ระหว่าง 26 - 50 สตางค์ ให้แสดงผลเป็น 50 สตางค์
หากเศษสตางค์อยู่ระหว่าง 51 - 75 สตางค์ ให้แสดงผลเป็น 75 สตางค์
หากเศษสตางค์อยู่ระหว่าง 76 - 99 สตางค์ ให้เลขจำนวนเต็มเพิ่มขึ้น 1 ส่วนเศษกลายเป็น 0 แทน ระหว่างค่า 1 - 75 สตางค์ (ทศนิยม) ค่าเลขจำนวนเต็ม (Integer) จะไม่มีการเปลี่ยนแปลงน่ะครับ
Conclusion: การศึกษาเรื่องราวของชนิดข้อมูล และ วิธีการจัดเก็บข้อมูลลงในคอมพิวเตอร์ให้ดีอย่างถ่องแท้ ก็จะสามารถช่วยให้เราเขียนโค้ดออกมาได้อย่างไม่มีปัญหา โดยเฉพาะสายงานทางด้านการเงิน งานด้านวิศวกรรม หรืองานทางด้านวิทยาศาสตร์ิ ที่ต้องใช้ความละเอียดของข้อมูลชนิดตัวเลขแบบทศนิยมสูงมาก ก็ต้องยิ่งระมัดระวังเป็นพิเศษอย่างยิ่ง ...
|
|