papermario/tools/splat/util/floats.py
Ethan Roseman 179998098c
Misc decomp 53 (#703)
* some btl_state work

* msg_draw_speech_bubble

* cleaners

* btl_state_stuff

* btl_state_update_next_enemy wip

* btl_state stuff

* disable_x fx + cleanup

* wip

* fxstuff

* path funcs & cleanup

* clean

* model_api funcs

* two action commands

* action_cmd progress

* UnkFunc001

* air raid func

* cleanup, data migration, goodies

* remove data file

* git subrepo pull --force tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "a847090eac"
upstream:
  origin:   "https://github.com/ethteck/splat.git"
  branch:   "master"
  commit:   "a847090eac"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

* fix build

* more cleanup

* clean

* PR comments
2022-05-05 23:08:16 +09:00

63 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 and "e" 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)