84 lines
2.0 KiB
Python
84 lines
2.0 KiB
Python
import decimal
|
|
from typing import (
|
|
Callable,
|
|
Tuple,
|
|
)
|
|
|
|
ABI_DECIMAL_PREC = 999
|
|
|
|
abi_decimal_context = decimal.Context(prec=ABI_DECIMAL_PREC)
|
|
|
|
ZERO = decimal.Decimal(0)
|
|
TEN = decimal.Decimal(10)
|
|
|
|
|
|
def ceil32(x: int) -> int:
|
|
return x if x % 32 == 0 else x + 32 - (x % 32)
|
|
|
|
|
|
def compute_unsigned_integer_bounds(num_bits: int) -> Tuple[int, int]:
|
|
return (
|
|
0,
|
|
2**num_bits - 1,
|
|
)
|
|
|
|
|
|
def compute_signed_integer_bounds(num_bits: int) -> Tuple[int, int]:
|
|
return (
|
|
-1 * 2 ** (num_bits - 1),
|
|
2 ** (num_bits - 1) - 1,
|
|
)
|
|
|
|
|
|
def compute_unsigned_fixed_bounds(
|
|
num_bits: int,
|
|
frac_places: int,
|
|
) -> Tuple[decimal.Decimal, decimal.Decimal]:
|
|
int_upper = compute_unsigned_integer_bounds(num_bits)[1]
|
|
|
|
with decimal.localcontext(abi_decimal_context):
|
|
upper = decimal.Decimal(int_upper) * TEN**-frac_places
|
|
|
|
return ZERO, upper
|
|
|
|
|
|
def compute_signed_fixed_bounds(
|
|
num_bits: int,
|
|
frac_places: int,
|
|
) -> Tuple[decimal.Decimal, decimal.Decimal]:
|
|
int_lower, int_upper = compute_signed_integer_bounds(num_bits)
|
|
|
|
with decimal.localcontext(abi_decimal_context):
|
|
exp = TEN**-frac_places
|
|
lower = decimal.Decimal(int_lower) * exp
|
|
upper = decimal.Decimal(int_upper) * exp
|
|
|
|
return lower, upper
|
|
|
|
|
|
def scale_places(places: int) -> Callable[[decimal.Decimal], decimal.Decimal]:
|
|
"""
|
|
Returns a function that shifts the decimal point of decimal values to the
|
|
right by ``places`` places.
|
|
"""
|
|
if not isinstance(places, int):
|
|
raise ValueError(
|
|
f"Argument `places` must be int. Got value {places} "
|
|
f"of type {type(places)}.",
|
|
)
|
|
|
|
with decimal.localcontext(abi_decimal_context):
|
|
scaling_factor = TEN**-places
|
|
|
|
def f(x: decimal.Decimal) -> decimal.Decimal:
|
|
with decimal.localcontext(abi_decimal_context):
|
|
return x * scaling_factor
|
|
|
|
places_repr = f"Eneg{places}" if places > 0 else f"Epos{-places}"
|
|
func_name = f"scale_by_{places_repr}"
|
|
|
|
f.__name__ = func_name
|
|
f.__qualname__ = func_name
|
|
|
|
return f
|