Source code for pythainlp.util.numtoword

# SPDX-FileCopyrightText: 2016-2026 PyThaiNLP Project
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: Apache-2.0
"""
Convert numbers to Thai text read out.

Adapted from:

* https://justmindthought.blogspot.com/2012/12/code-php.html
* https://suksit.com/post/writing-bahttext-in-php/
"""

from __future__ import annotations

import math
from typing import Optional

__all__: list[str] = ["bahttext", "num_to_thaiword", "num_to_thaiword_float"]

_VALUES: list[str] = [
    "",
    "หนึ่ง",
    "สอง",
    "สาม",
    "สี่",
    "ห้า",
    "หก",
    "เจ็ด",
    "แปด",
    "เก้า",
]
_PLACES: list[str] = ["", "สิบ", "ร้อย", "พัน", "หมื่น", "แสน", "ล้าน"]
_DIGITS: list[str] = [
    "ศูนย์",
    "หนึ่ง",
    "สอง",
    "สาม",
    "สี่",
    "ห้า",
    "หก",
    "เจ็ด",
    "แปด",
    "เก้า",
]
_EXCEPTIONS: dict[str, str] = {"หนึ่งสิบ": "สิบ", "สองสิบ": "ยี่สิบ", "สิบหนึ่ง": "สิบเอ็ด"}


[docs] def bahttext(number: float) -> str: """ Convert a number to Thai text in Baht currency format. Add the suffix "บาท" (Baht). The precision is fixed at two decimal places (0.00) to fit the "สตางค์" (Satang) unit. This function works similarly to the ``BAHTTEXT`` function in Microsoft Excel. :param float number: number to be converted :return: text representing the amount of money in Thai currency format :rtype: str :raises TypeError: if *number* is not a numeric type :Example: >>> from pythainlp.util import bahttext >>> bahttext(1) 'หนึ่งบาทถ้วน' >>> bahttext(21) 'ยี่สิบเอ็ดบาทถ้วน' >>> bahttext(200) 'สองร้อยบาทถ้วน' """ if not isinstance(number, (int, float)): raise TypeError( f"number must be a numeric type, not {type(number).__name__!r}" ) ret = "" if number == 0: ret = "ศูนย์บาทถ้วน" else: num_int_str, num_dec_str = f"{number:.2f}".split(".") num_int = int(num_int_str) num_dec = int(num_dec_str) baht = num_to_thaiword(num_int) if baht: ret = f"{ret}{baht}บาท" satang = num_to_thaiword(num_dec) if satang and satang != "ศูนย์": ret = f"{ret}{satang}สตางค์" else: ret = f"{ret}ถ้วน" return ret
def _num_to_thaiword_block(num: int) -> str: """ Convert a positive integer < 1,000,000 to Thai text. This is the core logic for a single block of up to 6 digits. """ if num == 0: return "" output = "" num_str = str(num) for place, value in enumerate(list(num_str[::-1])): if value != "0": output = _VALUES[int(value)] + _PLACES[place] + output for search, replac in _EXCEPTIONS.items(): output = output.replace(search, replac) # เอ็ด rule: trailing หนึ่ง in ones place if num != 1 and output.endswith("หนึ่ง"): output = output[: -len("หนึ่ง")] + "เอ็ด" return output
[docs] def num_to_thaiword(number: Optional[int]) -> str: """ Convert an integer to Thai text. :param Optional[int] number: integer to be converted :return: text representing the number in Thai :rtype: str :Example: >>> from pythainlp.util import num_to_thaiword >>> num_to_thaiword(1) 'หนึ่ง' >>> num_to_thaiword(11) 'สิบเอ็ด' """ if number is None: return "" if number == 0: return "ศูนย์" number_abs = abs(number) number_str = str(number_abs) # Split into groups of 6 digits from right side groups: list[str] = [] while number_str: group = number_str[-6:] number_str = number_str[:-6] groups.append(group) output = "" for i in range(len(groups) - 1, -1, -1): group_num = int(groups[i]) if group_num > 0: output += _num_to_thaiword_block(group_num) if i > 0: output += "ล้าน" # Global เอ็ด rule: trailing หนึ่ง in the full text if number_abs != 1 and output.endswith("หนึ่ง"): output = output[: -len("หนึ่ง")] + "เอ็ด" if number < 0: output = "ลบ" + output return output
def _expand_exponent(num_str: str) -> str: """Rewrite scientific notation (such as "1e-05") as a decimal string.""" if "e" not in num_str.lower(): return num_str mantissa, exp_str = num_str.lower().split("e", 1) mant_int, _, mant_frac = mantissa.partition(".") digits = mant_int + mant_frac shift = int(exp_str) - len(mant_frac) if shift >= 0: return digits + ("0" * shift) pos = len(digits) + shift if pos > 0: return digits[:pos] + "." + digits[pos:] return "0." + ("0" * (-pos)) + digits
[docs] def num_to_thaiword_float(number: float) -> str: """ Convert a floating-point number to Thai text. Convert the integer part with :func:`num_to_thaiword`. Read the decimal point as "จุด". Read each digit after the decimal point individually, without place descriptions. :param float number: number to be converted :return: text representing the number in Thai :rtype: str :raises TypeError: if *number* is not a numeric type :raises ValueError: if *number* is not finite :Example: >>> from pythainlp.util import num_to_thaiword_float >>> num_to_thaiword_float(123.45) 'หนึ่งร้อยยี่สิบสามจุดสี่ห้า' >>> num_to_thaiword_float(3.14159) 'สามจุดหนึ่งสี่หนึ่งห้าเก้า' """ if not isinstance(number, (int, float)): raise TypeError( f"number must be a numeric type, not {type(number).__name__!r}" ) # Reject non-finite floats early (nan/inf), since they cannot be rendered. if isinstance(number, float) and not math.isfinite(number): raise ValueError("number must be a finite float") if isinstance(number, int) or number.is_integer(): return num_to_thaiword(int(number)) num_str = _expand_exponent(str(abs(number))) if "." in num_str: int_part, dec_part = num_str.split(".") result = num_to_thaiword(int(int_part)) + "จุด" result += "".join(_DIGITS[int(digit)] for digit in dec_part) else: result = num_to_thaiword(int(num_str)) return "ลบ" + result if number < 0 else result