Last active
August 6, 2023 08:11
-
-
Save aaronfranke/8b2c39bfbe48068c574e4daf2710c875 to your computer and use it in GitHub Desktop.
IEEE-style floating-point format table generator (GDScript)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# IEEE-style floating-point format table generator (GDScript) | |
# Python version: https://gist.github.com/aaronfranke/0d1217e521c4ec784d39e92b5f039115 | |
# GDScript version: https://gist.github.com/aaronfranke/8b2c39bfbe48068c574e4daf2710c875 | |
extends Node | |
# SEMB values can describe any IEEE-like binary floating-point format. | |
var sign_bit: bool = true | |
var exponent_bits: int = 2 | |
var mantissa_bits: int = 1 | |
## Can be set to an integer. If null, the bias will be automatically determined | |
## to match IEEE formats, symmetric around 1. Specialized use cases may benefit | |
## from overriding this, but this should usually be kept as automatic. | |
var bias = null | |
func _ready() -> void: | |
assert(exponent_bits > 0, "Exponent bit amount cannot be zero or negative. A float must have at least 1 exponent bit, or else it's just an integer, loses Inf/NaN, etc.") | |
assert(mantissa_bits >= 0, "Mantissa bit amount cannot be negative. However, mantissa is allowed to have zero bits.") | |
if not bias is int: | |
bias = int(pow(2, exponent_bits - 1)) - 1 | |
# Generate the mantissa row without an exponent bias. | |
var mantissa_base = _generate_mantissa_base(mantissa_bits) | |
# Generate the cells of the table. | |
var table_cell_rows: Array[Array] = _generate_table_data_cells(mantissa_base) | |
# Format the cells as a WikiText table. | |
var wikitext: String = _format_pretty_table_wikitext(table_cell_rows) | |
print(wikitext) | |
func _format_pretty_table_wikitext(table_cell_rows: Array[Array]) -> String: | |
var output_text: String = '{| class="wikitable" style="text-align:right; font-size:small"\n!\n! ' | |
# Generate the header row. | |
var first_row: Array = table_cell_rows[0] | |
for i in range(first_row.size()): | |
var column_width = String(first_row[i]).length() | |
if i != 0: | |
output_text += " || " | |
output_text += ("… " + _int_to_bits(i, mantissa_bits)).lpad(column_width, " ") | |
# Generate the main rows. | |
var table_row_count: int = table_cell_rows.size() | |
for i in range(table_row_count): | |
output_text += "\n|-\n! " | |
if sign_bit: | |
@warning_ignore("integer_division") | |
output_text += "0 " if i < table_row_count / 2 else "1 " | |
output_text += _int_to_bits(i, exponent_bits) + " …\n" | |
var row: Array = table_cell_rows[i] | |
for j in range(row.size()): | |
if j == 0: | |
output_text += "| " | |
else: | |
output_text += " || " | |
output_text += row[j] | |
output_text += "\n|}" | |
return output_text | |
func _generate_table_data_cells(mantissa_base: Array[float]) -> Array[Array]: | |
var rows: Array[Array] = [] | |
var column_widths: Array[int] = [] | |
var exponent_range: int = int(pow(2, exponent_bits)) - 1 | |
# Generate the main rows. | |
for i in range(exponent_range): | |
var row: Array = [] | |
rows.append(row) | |
var exponent: int = i - bias | |
if i == 0: | |
exponent += 1 | |
var multiplier = pow(2, exponent) | |
for j in range(mantissa_base.size()): | |
var mantissa_number: float = mantissa_base[j] | |
if i == 0: | |
column_widths.append(_starting_column_width()) | |
mantissa_number -= 1.0 | |
var stringified_number = str(mantissa_number * multiplier) | |
if stringified_number.length() > column_widths[j]: | |
column_widths[j] = stringified_number.length() | |
if sign_bit: | |
column_widths[j] += 1 | |
row.append(stringified_number) | |
# Add the infinity/NaN row. | |
var inf_nan_row: Array = ["Inf"] | |
for i in range(mantissa_base.size() - 1): | |
inf_nan_row.append("NaN") | |
rows.append(inf_nan_row) | |
# If there is a sign bit, append a duplicate of every row, with a minus sign. | |
if sign_bit: | |
for row in rows.duplicate(true): | |
for cell_index in range(row.size()): | |
row[cell_index] = "−" + row[cell_index] | |
rows.append(row) | |
# Pad each column's entries to be the same length. | |
for i in range(rows.size()): | |
var row = rows[i] | |
for j in range(row.size()): | |
var column_width: int = column_widths[j] | |
row[j] = String(row[j]).lpad(column_width, " ") | |
return rows | |
func _starting_column_width() -> int: | |
var ret: int = 2 + mantissa_bits | |
if sign_bit: | |
if ret < 4: | |
return 4 | |
else: | |
if ret < 3: | |
return 3 | |
return ret | |
func _generate_mantissa_base(bits_amount: int) -> Array[float]: | |
var mantissa_base: Array[float] = [] | |
var mantissa_step: float = pow(2, -bits_amount) | |
var mantissa_value: float = 1.0 | |
while true: | |
mantissa_base.append(mantissa_value) | |
mantissa_value += mantissa_step | |
if mantissa_value >= 2.0: | |
break | |
return mantissa_base | |
func _int_to_bits(number: int, bits_amount: int) -> String: | |
var ret: String = "" | |
var digit_value: int = 1 | |
for i in range(bits_amount): | |
if number & digit_value == 0: | |
ret = "0" + ret | |
else: | |
ret = "1" + ret | |
digit_value *= 2 | |
return ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment