Functional Weave
Code in Python

math.basis-points@2.0.0

impl/python/convert_rate.py

1,558 bytes · the Python implementation · view raw

import re

from .math_basis_points_types import RateUnit

# Explicit [0-9]: Python's \d also matches other scripts' digits.
DECIMAL = re.compile(r"^-?[0-9]+(\.[0-9]+)?\Z")


def _exponent(unit: str) -> int:
    """Powers of ten between each unit and basis points."""
    if unit == "basis-points":
        return 0
    if unit == "percent":
        return 2
    if unit == "ratio":
        return 4
    raise ValueError('unknown rate unit "%s"' % (unit,))


def convert_rate(value: str, from_unit: RateUnit, to_unit: RateUnit) -> str:
    """Convert a rate between percent, basis points and a ratio.

    The units differ by powers of ten, so this moves the decimal point in the
    text itself: exact for any input, with no float anywhere.
    """
    if not isinstance(value, str) or not DECIMAL.match(value):
        raise ValueError('"%s" is not a decimal number' % (value,))
    shift = _exponent(from_unit) - _exponent(to_unit)

    negative = value.startswith("-")
    body = value[1:] if negative else value
    point = body.find(".")
    digits = body if point < 0 else body[:point] + body[point + 1 :]
    position = (len(body) if point < 0 else point) + shift

    if position > len(digits):
        digits = digits + "0" * (position - len(digits))
    if position < 0:
        digits = "0" * (-position) + digits
        position = 0

    whole = digits[:position].lstrip("0") or "0"
    fraction = digits[position:].rstrip("0")
    text = whole + "." + fraction if fraction else whole
    return "-" + text if negative and text != "0" else text