1,957 bytes · the Python implementation · view raw
import math
POW10 = [1.0, 10.0, 100.0, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12]
# 2^52: at or above this every double is a whole number, so a scaled value# this large has no digits left to round.
_INTEGRAL = 4503599627370496.0# Veltkamp's splitting constant, 2^27 + 1.
_SPLIT = 134217729.0def _product_error(a: float, b: float, p: float) -> float:
"""a * b - fl(a * b), exactly (Dekker's TwoProduct), with only * and -."""
ca = _SPLIT * a
ah = ca - (ca - a)
al = a - ah
cb = _SPLIT * b
bh = cb - (cb - b)
bl = b - bh
return ((ah * bh - p) + ah * bl + al * bh) + al * bl
def round_float(value: float, decimals: int) -> float:
"""Round half away from zero to ``decimals`` places, deciding on the exact value of the double. 2.675 is stored as 2.674999..., so it rounds to 2.67. Python's round() is half-even (round(0.125, 2) == 0.12); this gives 0.13. The product's rounding error is recovered exactly, so the tie test is on the true value, with the same operations in the same order as TypeScript and Rust. """if isinstance(value, bool) ornot isinstance(value, (int, float)) ornot math.isfinite(value):
raise TypeError("value must be a finite number, received %r" % (value,))
if isinstance(decimals, bool) ornot isinstance(decimals, int) or decimals < 0or decimals > 12:
raise ValueError("decimals must be a whole number from 0 to 12, received %r" % (decimals,))
scale = POW10[decimals]
a = abs(float(value))
y = a * scale
if y >= _INTEGRAL:
return float(value) + 0.0
r = float(math.floor(y))
# y - r is exact, and so is subtracting a half from it; adding the# product's error cannot change the sign, only settle a tie.
above = (y - r - 0.5) + _product_error(a, scale, y)
if above >= 0:
r += 1.0
out = r / scale
# + 0.0 turns -0.0 into 0.0.return (-out if value < 0else out) + 0.0