IsDecimalMagnitudeBelow🔗
Check that the magnitude of the decimal
text is below limit.
The text is expected to match the pattern of a finite xs:double
or xs:float,
i.e.
, a decimal with an optional sign, an optional point and
an optional exponent. The limit is expected to be a positive integer
without leading zeros.
We compare the numbers as strings, since they do not necessarily fit into any native number, and parsing them to floating-point numbers would round them.
Code
unsigned = text.lstrip("+-")
exponent_start = unsigned.find("e")
if exponent_start == -1:
exponent_start = unsigned.find("E")
mantissa = unsigned
exponent_text = ""
if exponent_start != -1:
mantissa = unsigned[:exponent_start]
exponent_text = unsigned[exponent_start + 1 :]
point = mantissa.find(".")
integer_part = mantissa
fraction_part = ""
if point != -1:
integer_part = mantissa[:point]
fraction_part = mantissa[point + 1 :]
# NOTE (mristin):
# We represent the decimal as the significant digits ``0.d1 d2 ...`` times
# ten to the power of the magnitude. The significant digits are given by
# the ``head`` followed by the ``tail``.
head = integer_part.lstrip("0")
tail = fraction_part
shift: int = len(head)
if head == "":
tail = fraction_part.lstrip("0")
if tail == "":
# The decimal is zero.
return True
shift = len(tail) - len(fraction_part)
exponent_digits = exponent_text.lstrip("+-").lstrip("0")
# NOTE (mristin):
# An exponent with more than 15 digits does not safely fit into an integer.
# Its magnitude then dwarfs the number of the digits in any text, so its sign
# alone decides.
if len(exponent_digits) > 15:
return exponent_text[:1] == "-"
exponent: int = 0
if len(exponent_digits) > 0:
exponent = int(exponent_digits)
if exponent_text[:1] == "-":
exponent = -exponent
magnitude = exponent + shift
if magnitude != len(limit):
return magnitude < len(limit)
for i in range(0, len(limit)):
digit = _digit_at(head, tail, i)
limit_digit = int(limit[i : i + 1])
if digit != limit_digit:
return digit < limit_digit
# NOTE (mristin):
# The decimal starts with all the digits of the limit, so it is at least as
# large as the limit.
return False