papermario/tools/splat/util/floats.py
Ethan Roseman dc72859546 rename
2021-01-25 00:19:48 +09:00

62 lines
2.3 KiB
Python

import math
import struct
# From mips_to_c: https://github.com/matt-kempster/mips_to_c/blob/d208400cca045113dada3e16c0d59c50cdac4529/src/translate.py#L2085
def format_f32_imm(num: int) -> str:
packed = struct.pack(">I", num & (2 ** 32 - 1))
value = struct.unpack(">f", packed)[0]
if not value or value == 4294967296.0:
# Zero, negative zero, nan, or INT_MAX.
return str(value)
# Write values smaller than 1e-7 / greater than 1e7 using scientific notation,
# and values in between using fixed point.
if abs(math.log10(abs(value))) > 6.9:
fmt_char = "e"
elif abs(value) < 1:
fmt_char = "f"
else:
fmt_char = "g"
def fmt(prec: int) -> str:
"""Format 'value' with 'prec' significant digits/decimals, in either scientific
or regular notation depending on 'fmt_char'."""
ret = ("{:." + str(prec) + fmt_char + "}").format(value)
if fmt_char == "e":
return ret.replace("e+", "e").replace("e0", "e").replace("e-0", "e-")
if "e" in ret:
# The "g" format character can sometimes introduce scientific notation if
# formatting with too few decimals. If this happens, return an incorrect
# value to prevent the result from being used.
#
# Since the value we are formatting is within (1e-7, 1e7) in absolute
# value, it will at least be possible to format with 7 decimals, which is
# less than float precision. Thus, this annoying Python limitation won't
# lead to us outputting numbers with more precision than we really have.
return "0"
return ret
# 20 decimals is more than enough for a float. Start there, then try to shrink it.
prec = 20
while prec > 0:
prec -= 1
value2 = float(fmt(prec))
if struct.pack(">f", value2) != packed:
prec += 1
break
if prec == 20:
# Uh oh, even the original value didn't format correctly. Fall back to str(),
# which ought to work.
return str(value)
ret = fmt(prec)
if "." not in ret:
ret += ".0"
return ret
def format_f64_imm(num: int) -> str:
(value,) = struct.unpack(">d", struct.pack(">Q", num & (2 ** 64 - 1)))
return str(value)