หากได้ทำงานเกี่ยวกับระบบด้วยการเงินแล้วเนี่ย ตัวเลขสำคัญมากนะครับ ปัญหาของผมเกิดจากการพัฒนาระบบงานบน .NET ด้วย C# แล้วต้องไปเทียบตัวเลขกับ ระบบงานที่เป็น VB6 แล้วปรากฏว่า มันหายไป 0.01 เลขปริศนาที่หายไป แต่ฝ่ายบัญชีไม่ยอมมมมมม ถ้าหายไปสัก 50 ล้าน มันยังหาได้ง่ายกว่าเลย
แล้วใครหละที่เป็นโจร ?
จริงแล้วมันเป็นปัญหาที่สะสมมานานตั้งแต่ของ VB6 ที่มีการปัดทศนิยมเศษ 5 แปลกๆ ทำให้ทีมที่ทำระบบเดิมในยุคนั้นมีการ Custom Function ขึ้นมาแก้ปัญหาจุจิกนี้ โดยเฉพาะครับ ซึ่งมีแนวคิดว่าเจอ 5 ปัดขึ้น สำหรับแนวคิดนี้นิยมใช้ในการบัญชี การเงินครับ
'================================================================================= 'Function : Round5Up 'Purpose : Return rounded Number. 'Direction : This function solve round function in VB about odd and even value. '================================================================================= Public Function Round5Up(ByVal pvarNum As Variant, ByVal plngPos As Long) As Variant On Error GoTo LineFail Dim lstrNum As String Dim lstrDecimal As String Dim llngB4Decimal As Long Dim i As Long lstrNum = Trim(CStr(CDec(pvarNum))) For i = 1 To Len(lstrNum) If Mid(lstrNum, i, 1) = "." Then llngB4Decimal = i lstrDecimal = Mid(lstrNum, i + 1, Len(lstrNum) - i) If plngPos + 1 > Len(lstrDecimal) Then GoTo LineFail End If If Mid(lstrDecimal, plngPos + 1, 1) = "5" Then Round5Up = Round(Mid(lstrNum, 1, llngB4Decimal + plngPos) + "6", plngPos) Else Round5Up = Round(CDec(lstrNum), plngPos) End If GoTo LineExit End If Next i If i - 1 = Len(lstrNum) Then GoTo LineFail End If LineExit: Round5Up = CDec() Exit Function LineFail: If Not IsNumeric(lstrNum) Then Round5Up = 0 Else Round5Up = CDec(lstrNum) End If End Function
- หลังจากดู Legacy Code เรามาดูอัลกอริทึมที่ VB6 ใช้ดีกว่าครับ เจ้า VB6 ใช้อัลกอริทึมในการ Rounding คือ Banker's Rounding สาระสำคัญ คือ ต่ำกว่า 4 ปัดลง มากกว่า 6 ปัดขึ้น ปัญหา คือ เลข 5 เนี่ยแหละ ต้องดูตัวเลขข้างหน้า 5 หากเป็นเลขคี่ ก็ให้ปัดขึ้น หากไม่ใช่ก็ให้ปัดเศษทิ้ง (นายแบงค์นี่น่าเลือดจริงๆครับ)
- ระบบเดิมที่ผมไป Cross Check ใช้ Function Round5up ที่แก้ปัญหานี้เรียบร้อยแล้ว
ขอเสริม อีกนิดนึง คือ ในคอม ถ้าเราป้อน 1.50 มันไม่ได้เก็บ 1.50 ลงไปนะครับ มันแปลงเป็นเลขฐาน 2 เนี่ยแหละ โดยอาจจะเป็น 1.4999999999999 แล้วมาใช้อัลกอริทึมในการ Rounding อีกทีนะครับ (เดี๋ยวไปเขียน Blog แยกไปอธิบายอีกทีครับ)
จริงๆ .NET กับ VBA ก็ใช้วิธี Banker's Rounding ในการ Round นะครับ
หลายคนคงคิดว่าปัญหาทศนิยม Diff คงไม่จบแล้ว แต่จริง Microsoft .Net Framework ได้แก้ปัญานี้เรียบร้อยแล้ว ตั้งแต่ .NET Framework 2.0 แล้วครับ โดยเพิ่ม Enum อีกตัวนึงให้กับ Function Math.round ชิ้อ MidPointRounding โดยมี Config 2 ตัว ดังนี้
- AwayFromZero : ถ้าเจอ 5 ให้ปัดขึ้น หรือปัดหนีออกจากศูนย์ เหมือนกับ Function Round5up ใน VB6
- ToEven : ปัดไปหาเลขคู่
มาลองตัวอย่างการ Round ดีกว่า
Math.Round(3.145,2) //3.14 Math.Round(3.155,2) //3.16 //ลองเพิ่ม MidPointRounding.AwayFromZero Math.Round(3.145, 2, MidPointRounding.AwayFromZero) //3.15 Math.Round(3.155, 2, MidPointRounding.AwayFromZero) //3.16
หวังว่าบทความนี้จะช่วยผู้อ่านหลายๆคนที่ประสบปัญหากับเรื่องเศษทศนิยม Diff นะครับ
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.