numeric.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import decimal
  2. from typing import (
  3. Callable,
  4. Tuple,
  5. )
  6. ABI_DECIMAL_PREC = 999
  7. abi_decimal_context = decimal.Context(prec=ABI_DECIMAL_PREC)
  8. ZERO = decimal.Decimal(0)
  9. TEN = decimal.Decimal(10)
  10. def ceil32(x: int) -> int:
  11. return x if x % 32 == 0 else x + 32 - (x % 32)
  12. def compute_unsigned_integer_bounds(num_bits: int) -> Tuple[int, int]:
  13. return (
  14. 0,
  15. 2**num_bits - 1,
  16. )
  17. def compute_signed_integer_bounds(num_bits: int) -> Tuple[int, int]:
  18. return (
  19. -1 * 2 ** (num_bits - 1),
  20. 2 ** (num_bits - 1) - 1,
  21. )
  22. def compute_unsigned_fixed_bounds(
  23. num_bits: int,
  24. frac_places: int,
  25. ) -> Tuple[decimal.Decimal, decimal.Decimal]:
  26. int_upper = compute_unsigned_integer_bounds(num_bits)[1]
  27. with decimal.localcontext(abi_decimal_context):
  28. upper = decimal.Decimal(int_upper) * TEN**-frac_places
  29. return ZERO, upper
  30. def compute_signed_fixed_bounds(
  31. num_bits: int,
  32. frac_places: int,
  33. ) -> Tuple[decimal.Decimal, decimal.Decimal]:
  34. int_lower, int_upper = compute_signed_integer_bounds(num_bits)
  35. with decimal.localcontext(abi_decimal_context):
  36. exp = TEN**-frac_places
  37. lower = decimal.Decimal(int_lower) * exp
  38. upper = decimal.Decimal(int_upper) * exp
  39. return lower, upper
  40. def scale_places(places: int) -> Callable[[decimal.Decimal], decimal.Decimal]:
  41. """
  42. Returns a function that shifts the decimal point of decimal values to the
  43. right by ``places`` places.
  44. """
  45. if not isinstance(places, int):
  46. raise ValueError(
  47. f"Argument `places` must be int. Got value {places} "
  48. f"of type {type(places)}.",
  49. )
  50. with decimal.localcontext(abi_decimal_context):
  51. scaling_factor = TEN**-places
  52. def f(x: decimal.Decimal) -> decimal.Decimal:
  53. with decimal.localcontext(abi_decimal_context):
  54. return x * scaling_factor
  55. places_repr = f"Eneg{places}" if places > 0 else f"Epos{-places}"
  56. func_name = f"scale_by_{places_repr}"
  57. f.__name__ = func_name
  58. f.__qualname__ = func_name
  59. return f