ทดสอบไลบรารีแปลงเงินบาทเป็นคำอ่าน 4 ตัว กับจำนวนเงิน 3,289 จำนวน เทียบ LibreOffice
เราเทียบ thai-baht-text, bahttext (npm), PyThaiNLP และ bahttext (PyPI) รุ่นล่าสุด กับ BAHTTEXT ของ LibreOffice จำนวนเงินทั่วไปอ่านตรงกันเกือบหมด แต่ทุกตัวมีกรณีที่ต้องระวัง: ศูนย์ เลขติดลบ หนึ่งสตางค์ และทศนิยมสามตำแหน่ง
ใบเสร็จ ใบกำกับภาษี และเช็ค ต้องเขียนจำนวนเงินเป็นตัวหนังสือ เช่น 1,234 บาท เป็น “หนึ่งพันสองร้อยสามสิบสี่บาทถ้วน” นักพัฒนาไทยส่วนใหญ่ไม่เขียนเอง แต่ติดตั้งไลบรารีสำเร็จรูป ตอนสร้างเครื่องมือแปลงเงินบาทเป็นคำอ่านของเว็บนี้ เราอยากรู้ว่าไลบรารีที่คนใช้กันอ่านตรงกันแค่ไหน จึงทดสอบรุ่นล่าสุดสี่ตัวกับจำนวนเงิน 3,289 จำนวน
ผลโดยรวมดีกว่าที่คาด จำนวนเงินทั่วไปอ่านถูกเกือบหมด แต่ทุกตัวมีกรณีเฉพาะที่ต้องระวัง และถ้าไม่รู้ก็จะหลุดไปถึงเอกสารของลูกค้า
ทดสอบอย่างไร
ตัวอ้างอิง คือ BAHTTEXT ของ LibreOffice Calc 24.2 ซึ่งเป็นฟังก์ชันที่ LibreOffice ทำตาม Excel (ในมาตรฐาน OpenDocument ฟังก์ชันนี้ชื่อ COM.MICROSOFT.BAHTTEXT) เราไม่มี Excel ให้เทียบ จึงไม่ได้ยืนยันว่า Excel ให้ผลตรงกันทุกจำนวน เรารัน LibreOffice แบบไม่เปิดหน้าต่าง (headless) แล้วเก็บคำอ่านของทุกจำนวนไว้เป็นไฟล์เดียว
จำนวนเงิน 3,289 จำนวน นับตามค่า แบ่งเป็นจำนวนเต็ม 1,269 จำนวน ทศนิยมหนึ่งตำแหน่ง 665 จำนวน สองตำแหน่ง 1,055 จำนวน และสามตำแหน่ง 300 จำนวน มีทั้งกรณีพิเศษอย่าง 0, 1, 11, 21, 101, 1,000,001 และจำนวนหลักล้านล้าน แต่ไม่มีเลขติดลบ เลขติดลบเราลองแยกเป็นรายตัว
ไลบรารีที่ทดสอบ (รุ่นล่าสุด ณ 24 กันยายน 2026)
| ไลบรารี | ภาษา |
|---|---|
| thai-baht-text 2.0.5 (npm) | JavaScript |
| bahttext 2.4.0 (npm, jojoee) | JavaScript |
PyThaiNLP 5.3.7 (pythainlp.util.bahttext) | Python |
| bahttext 1.0.2 (PyPI) | Python |
ไฟล์ผลของ LibreOffice ทั้ง 3,289 จำนวนดาวน์โหลดได้ที่นี่ (TSV สองคอลัมน์: จำนวนเงิน, คำอ่าน) ใช้เป็นชุดทดสอบของไลบรารีตัวไหนก็ได้
สองเรื่องที่ไม่ใช่บั๊ก
ก่อนนับว่าอะไรผิด ต้องแยกเรื่องที่เป็นแค่ “คนละแบบ” ออกก่อน
เอ็ด หรือ หนึ่ง ราชบัณฑิตยสภาอธิบายว่า เลข ๑ ท้ายจำนวนตั้งแต่ ๒ หลักขึ้นไป ตามหลักภาษาไทยให้ออกเสียงว่า เอ็ด ได้เพียงอย่างเดียว ตัวอย่างคือ ๑๐๑ อ่านว่า “หนึ่งร้อยเอ็ด” และ ๕๐๑,๗๔๑,๒๒๑ อ่านว่า “ห้าร้อยเอ็ดล้าน…” ส่วนการอ่านว่า “หนึ่ง” ที่ยังพบกัน ราชบัณฑิตยสภาสันนิษฐานว่ามาจากการสื่อสารของทหารเรือ ที่ต้องกันฟังผิดท่ามกลางเสียงคลื่นและเครื่องยนต์ (หลักเดียวกันอีกหน้า) แต่ LibreOffice ใช้ “เอ็ด” เฉพาะเมื่อหลักสิบไม่เป็นศูนย์ จึงอ่าน 101 ว่า “หนึ่งร้อยหนึ่ง” เราใช้ LibreOffice เป็นตัวเทียบ เพราะเป็นโปรแกรมที่คนใช้ออกเอกสารจริง ไม่ได้แปลว่ามันถูกกว่า ไลบรารีที่อ่าน “หนึ่งร้อยเอ็ด” ตรงกับหลักราชบัณฑิตยสภา เราจึงไม่นับความต่างนี้เป็นบั๊ก
ขึ้นต้นด้วย “ศูนย์บาท” PyThaiNLP และ bahttext ของ PyPI อ่าน 0.9 ว่า “ศูนย์บาทเก้าสิบสตางค์” ขณะที่ LibreOffice อ่านว่า “เก้าสิบสตางค์” เป็นเรื่องรูปแบบ ไม่ใช่ความผิด
ผล: จำนวนเงินทั่วไปอ่านตรงกัน
กับจำนวนเงินทศนิยมไม่เกินสองตำแหน่ง 2,989 จำนวน เมื่อไม่นับสองเรื่องข้างบน ไลบรารีทั้งสี่อ่านตรงกับ LibreOffice เกือบทุกจำนวน ที่ต่างคือ thai-baht-text กับค่าศูนย์ 2 แถว (0 และ 0.0), bahttext ของ PyPI กับจำนวนที่ลงท้ายด้วย .01 ทั้ง 15 จำนวน และ bahttext ของ npm กับ 11 จำนวนที่หลักล้านลงท้ายด้วย 01 เช่น 701,314,828.05
สิ่งที่พบทั้งหมด รวมเลขติดลบที่ลองแยก
| ไลบรารี | สิ่งที่พบ | ตัวอย่าง |
|---|---|---|
| thai-baht-text 2.0.5 | 0 ได้ข้อความว่าง | 0 → "" |
| thai-baht-text 2.0.5 | ไม่รองรับเลขติดลบ ได้คำว่า undefined และหลักเลื่อน ส่วนค่าระหว่าง -1 กับ 0 ออกมาเป็นบวก | -5 → "undefinedสิบห้าบาทถ้วน", -100 → "undefinedพันหนึ่งร้อยบาทถ้วน", -0.5 → "ห้าสิบสตางค์" |
| bahttext 1.0.2 (PyPI) | หนึ่งสตางค์อ่านเป็น “เอ็ดสตางค์” ทั้งที่ 1 สตางค์เป็นเลขหลักเดียว ซึ่งกฎ “เอ็ด” ไม่ครอบคลุม | 671.01 → “หกร้อยเจ็ดสิบเอ็ดบาทเอ็ดสตางค์” |
| PyThaiNLP 5.3.7 | ทิ้งเครื่องหมายลบเมื่อค่าอยู่ระหว่าง -1 กับ 0 | -0.5 → “ศูนย์บาทห้าสิบสตางค์” (ส่วน -1.5 อ่านถูกว่า “ลบหนึ่งบาทห้าสิบสตางค์”) |
| bahttext 2.4.0 (npm) | ใช้กฎเอ็ดไม่คงเส้นคงวา (คำอ่านแต่ละแบบถูกตามหลักใดหลักหนึ่ง แต่ไม่ใช้หลักเดียวกันทั้งตัวเลข) | 101 → “หนึ่งร้อยหนึ่ง” แต่ 101000000 → “หนึ่งร้อยเอ็ดล้าน” |
ข้อสุดท้าย README ของ bahttext (npm) บอกว่าทดสอบกับ BAHTTEXT ของ Google Sheets เราจึงลองใน Google Sheets ได้ =BAHTTEXT(101000000) → “หนึ่งร้อยหนึ่งล้านบาทถ้วน” (ตรวจ 24 กันยายน 2026) ตรงกับ LibreOffice ไลบรารีจึงต่างจากตัวอ้างอิงของตัวเอง ทั้งที่คำอ่าน “หนึ่งร้อยเอ็ดล้าน” เป็นแบบที่ราชบัณฑิตยสภาให้ใช้ (ตัวอย่างของราชบัณฑิตยสภาเองคือ “ห้าร้อยเอ็ดล้าน”) ส่วนที่ไม่ตามหลักคือการอ่าน 101 ว่า “หนึ่งร้อยหนึ่ง” ของไลบรารีเอง ปัญหาจึงอยู่ที่ความไม่คงเส้นคงวา ไม่ใช่ภาษาไทยผิด
เรื่องเลขติดลบ README ของ thai-baht-text ไม่ได้บอกว่ารองรับ ส่วน bahttext (npm) บอกว่ารองรับ และอ่านถูกทุกค่าที่เราลอง ถ้าระบบของคุณไม่มีทางส่ง 0 หรือเลขติดลบเข้าไป ข้อเหล่านี้ก็ไม่กระทบ แต่ใบลดหนี้ที่ยอดติดลบ หรือบรรทัดที่ยอดเป็นศูนย์ เกิดได้จริง
ทศนิยมสามตำแหน่ง: สองภาษา สองวิธี
เมื่อจำนวนเงินมีทศนิยมสามตำแหน่ง ไลบรารีแบ่งเป็นสองคู่ที่ให้ผลตรงกันภายในคู่ทุกจำนวน
| ไลบรารี | วิธีจัดการตำแหน่งที่สาม | ต่างจาก LibreOffice จริง (จาก 300 จำนวน) |
|---|---|---|
| thai-baht-text 2.0.5 | ตัดทิ้ง | 300 |
| bahttext 2.4.0 (npm) | ตัดทิ้ง | 300 |
| PyThaiNLP 5.3.7 | ปัดจากเลขฐานสอง | 156 |
| bahttext 1.0.2 (PyPI) | ปัดจากเลขฐานสอง | 156 |
LibreOffice ปัดครึ่งขึ้น ส่วนจำนวนสามตำแหน่งทั้ง 300 จำนวนในชุดทดสอบลงท้ายด้วย 5 ซึ่งเป็นกรณีกึ่งกลางพอดีที่วิธีต่าง ๆ ให้ผลต่างกันมากที่สุด ถ้าตัวท้ายเป็น 1 ถึง 4 การตัดทิ้งกับการปัดให้ผลเท่ากัน
ตัวอย่าง 3,282.995 LibreOffice ปัดเป็น “สามพันสองร้อยแปดสิบสามบาทถ้วน” แต่ไลบรารี JavaScript ตัดเหลือ “สามพันสองร้อยแปดสิบสองบาทเก้าสิบเก้าสตางค์” และ 2,454.495 LibreOffice ได้ห้าสิบสตางค์ ขณะที่ PyThaiNLP ได้สี่สิบเก้าสตางค์ เพราะในเลขฐานสอง ค่านี้ถูกเก็บเป็น 2,454.49499…
ไลบรารี JavaScript ทั้งสองตัวรับค่าเป็นข้อความได้ แต่ก็ยังตัดทิ้งเหมือนเดิม ทางแก้สำหรับทุกตัวจึงเหมือนกัน คือ ปัดเป็นสองตำแหน่งด้วยเลขฐานสิบเองก่อนเรียก เช่นใน Python
from decimal import Decimal, ROUND_HALF_UP
from pythainlp.util import bahttext
amount = Decimal("2454.495").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(bahttext(float(amount))) # สองพันสี่ร้อยห้าสิบสี่บาทห้าสิบสตางค์
ถ้าจำนวนเงินมาจากฐานข้อมูลเป็นชนิด DECIMAL สองตำแหน่งอยู่แล้ว ปัญหานี้แทบไม่เกิด
ลองเอง
JavaScript:
const thaiBahtText = require('thai-baht-text');
const { bahttext } = require('bahttext');
console.log(JSON.stringify(thaiBahtText(0))); // ""
console.log(thaiBahtText(-5)); // undefinedสิบห้าบาทถ้วน
console.log(bahttext(101)); // หนึ่งร้อยหนึ่งบาทถ้วน
console.log(bahttext(101000000)); // หนึ่งร้อยเอ็ดล้านบาทถ้วน
Python:
from bahttext import bahttext
from pythainlp.util import bahttext as pythainlp_bahttext
print(bahttext(671.01)) # หกร้อยเจ็ดสิบเอ็ดบาทเอ็ดสตางค์
print(pythainlp_bahttext(-0.5)) # ศูนย์บาทห้าสิบสตางค์
ข้อแนะนำ
- ตัดสินใจเรื่องเอ็ดหรือหนึ่งก่อน ถ้าต้องการตามราชบัณฑิตยสภา ให้เลือกไลบรารีที่อ่าน “หนึ่งร้อยเอ็ด” แล้วเขียนเทสต์ยืนยันไว้
- ปัดเป็นสองตำแหน่งด้วยเลขฐานสิบเองก่อนเรียก อย่าส่งทศนิยมยาวเข้าไป
- ทดสอบ 0, เลขติดลบ (รวมค่าระหว่าง -1 กับ 0) และ .01 ก่อนใช้กับเอกสารจริง กรณีเหล่านี้คือที่ที่เราเจอปัญหา
- ถ้าต้องการชุดทดสอบ ใช้ไฟล์ 3,289 จำนวนจาก LibreOfficeได้เลย
ไลบรารีทั้งสี่ทำงานดีกับงานส่วนใหญ่ เราไม่ได้บอกว่าตัวไหนไม่ควรใช้ แค่ต้องรู้ว่าตัวที่เลือกจัดการกรณีเหล่านี้อย่างไร เครื่องมือของเว็บนี้ใช้ในเบราว์เซอร์ได้ทันที คำนวณจากตัวเลขที่พิมพ์โดยตรงไม่ผ่านเลขฐานสอง ปัดสตางค์ครึ่งขึ้น และเลือกได้ทั้งแบบ “เอ็ด” และ “หนึ่ง”: แปลงเงินบาทเป็นคำอ่าน
ทดสอบเมื่อ 24 กันยายน 2026 กับรุ่นที่ระบุข้างบน รุ่นใหม่กว่านี้อาจแก้ไปแล้ว ไฟล์ข้อมูลเผยแพร่ภายใต้ CC BY 4.0 เช่นเดียวกับบทความ