#!/usr/bin/env python3 import math import re import decimal from typing import Set si_table = { -8: ("yocto", "y", "y", 10e-24), -7: ("zepto", "z", "z", 10e-21), -6: ("atto", "a", "a", 10e-18), -5: ("femto", "f", "f", 10e-15), -4: ("pico", "p", "p", 10e-12), -3: ("nano", "n", "n", 10e-9), -2: ("micro", "u", "μ", 10e-6), -1: ("milli", "m", "m", 10e-3), 0: ("", "", "", 0), 1: ("kilo", "k", "k", 10e3), 2: ("mega", "M", "M", 10e6), 3: ("giga", "G", "G", 10e9), 4: ("tera", "T", "T", 10e12), 5: ("peta", "P", "P", 10e15), 6: ("exa", "E", "E", 10e18), 7: ("zetta", "Z", "Z", 10e21), 8: ("yotta", "Y", "Y", 10e24), } _si_letters_set: Set[str] = set() for n in si_table: _si_letters_set.add(si_table[n][1]) _si_letters_set.add(si_table[n][2]) si_letters = "".join(sorted(_si_letters_set)) def prefix_tuple(value, utf=True): value = float(value) ex = math.log(abs(value * 1.0000001), 10) nex = math.floor(ex) // 3 nval = value / 10 ** (nex * 3) if nex not in si_table: raise ValueError("No suitable SI prefix found") if utf == True: px = si_table[nex][2] else: px = si_table[nex][1] return (nval, px) def prefix(value, utf=True, replace_separator=True): v, px = prefix_tuple(value, utf) if px == "": return "{:1.2f}".format(v).rstrip("0").rstrip(".") elif replace_separator == True: s = "{:1.2f}".format(v).replace(".", px).rstrip("0").rstrip(".") return s else: s = "{:1.2f}".format(v).rstrip("0").rstrip(".") return s + " {:s}".format(px) def power_by_prefix(string): for v in si_table: if string in si_table[v]: return v raise ValueError() def decode_infix(string): np = "^(([1-9][0-9]*)?[0-9](\.[0-9]+)?) ?([{:s}])?$".format(si_letters) ni = "^(([1-9][0-9]*)?[0-9](([{:s}])[0-9]+)?)$".format(si_letters) rp = re.match(np, string) ri = re.match(ni, string) if rp is not None: if rp.groups()[3] is not None: power = power_by_prefix(rp.groups()[3]) else: power = 0 base = decimal.Decimal(rp.groups()[0]) return base * decimal.Decimal("10") ** (power * 3) elif ri is not None: base = decimal.Decimal(str(ri.groups()[0]).replace(ri.groups()[3], ".")) power = power_by_prefix(ri.groups()[3]) return base * decimal.Decimal("10") ** (power * 3) else: raise ValueError() if __name__ == "__main__": print(prefix(1.5632e11)) print(prefix(12.32e-9)) print(prefix(47000)) print(prefix(4700)) print(prefix(470)) print(si_letters) print(decode_infix("0.23")) print(decode_infix("470M")) print(prefix(float(decode_infix("0k23"))))