Change project organization
This commit is contained in:
240
core/big_number.gd
Normal file
240
core/big_number.gd
Normal file
@@ -0,0 +1,240 @@
|
||||
class_name BigNumber
|
||||
extends RefCounted
|
||||
|
||||
## The core value representation.
|
||||
## For example, 1.5e24 is represented as mantissa = 1.5, exponent = 24.
|
||||
var mantissa: float
|
||||
var exponent: int
|
||||
|
||||
## Pre-calculate this to avoid dividing logs constantly
|
||||
const LOG10_E: float = 0.4342944819032518
|
||||
|
||||
func _init(m: float = 0.0, e: int = 0) -> void:
|
||||
mantissa = m
|
||||
exponent = e
|
||||
_normalize()
|
||||
|
||||
## Adjusts the internal representation so the mantissa is always between 1.0 and 9.99...
|
||||
## or exactly 0.0.
|
||||
func _normalize() -> void:
|
||||
if mantissa == 0.0:
|
||||
exponent = 0
|
||||
return
|
||||
|
||||
var is_negative: bool = mantissa < 0.0
|
||||
var abs_m: float = abs(mantissa)
|
||||
|
||||
if abs_m >= 10.0 or abs_m < 1.0:
|
||||
# Calculate how many powers of 10 we need to shift
|
||||
# Godot's log() is base 'e', so we multiply by log10(e) to get log10(x)
|
||||
var exp_diff: int = floori(log(abs_m) * LOG10_E)
|
||||
abs_m /= pow(10.0, float(exp_diff))
|
||||
exponent += exp_diff
|
||||
|
||||
mantissa = abs_m if not is_negative else -abs_m
|
||||
|
||||
# ==========================================
|
||||
# MATH OPERATIONS
|
||||
# ==========================================
|
||||
|
||||
func add(other: BigNumber) -> BigNumber:
|
||||
if mantissa == 0: return BigNumber.new(other.mantissa, other.exponent)
|
||||
if other.mantissa == 0: return BigNumber.new(mantissa, exponent)
|
||||
|
||||
var exp_diff: int = exponent - other.exponent
|
||||
|
||||
# If the difference in magnitude is massive, the smaller number is insignificant
|
||||
# (15 is a safe threshold for 64-bit float precision).
|
||||
if exp_diff >= 15:
|
||||
return BigNumber.new(mantissa, exponent)
|
||||
elif exp_diff <= -15:
|
||||
return BigNumber.new(other.mantissa, other.exponent)
|
||||
|
||||
var new_m: float = mantissa
|
||||
var new_e: int = exponent
|
||||
|
||||
# Scale the smaller number down to match the larger number's exponent
|
||||
if exp_diff > 0:
|
||||
new_m += other.mantissa / pow(10.0, float(exp_diff))
|
||||
elif exp_diff < 0:
|
||||
new_m = (mantissa / pow(10.0, float(-exp_diff))) + other.mantissa
|
||||
new_e = other.exponent
|
||||
else:
|
||||
new_m += other.mantissa
|
||||
|
||||
return BigNumber.new(new_m, new_e)
|
||||
|
||||
## High-performance addition for the _process() loop.
|
||||
## Modifies THIS instance instead of creating a new RefCounted object.
|
||||
func add_in_place(other: BigNumber) -> void:
|
||||
if other.mantissa == 0.0: return
|
||||
if mantissa == 0.0:
|
||||
mantissa = other.mantissa
|
||||
exponent = other.exponent
|
||||
return
|
||||
|
||||
var exp_diff: int = exponent - other.exponent
|
||||
|
||||
if exp_diff >= 15:
|
||||
return # Other number is too small to matter
|
||||
elif exp_diff <= -15:
|
||||
# This number is effectively replaced by the larger other number
|
||||
mantissa = other.mantissa
|
||||
exponent = other.exponent
|
||||
return
|
||||
|
||||
if exp_diff > 0:
|
||||
mantissa += other.mantissa / pow(10.0, float(exp_diff))
|
||||
elif exp_diff < 0:
|
||||
mantissa = (mantissa / pow(10.0, float(-exp_diff))) + other.mantissa
|
||||
exponent = other.exponent
|
||||
else:
|
||||
mantissa += other.mantissa
|
||||
|
||||
# Prevent floating-point drift near zero
|
||||
if abs(mantissa) < 0.0000000001:
|
||||
mantissa = 0.0
|
||||
exponent = 0
|
||||
else:
|
||||
_normalize()
|
||||
|
||||
func subtract(other: BigNumber) -> BigNumber:
|
||||
# Subtraction is just adding a negative number
|
||||
var negative_other = BigNumber.new(-other.mantissa, other.exponent)
|
||||
return add(negative_other)
|
||||
|
||||
func multiply(other: BigNumber) -> BigNumber:
|
||||
var new_m: float = mantissa * other.mantissa
|
||||
var new_e: int = exponent + other.exponent
|
||||
return BigNumber.new(new_m, new_e)
|
||||
|
||||
func divide(other: BigNumber) -> BigNumber:
|
||||
if other.mantissa == 0.0:
|
||||
push_error("BigNumber: Division by zero!")
|
||||
return BigNumber.new(0.0, 0)
|
||||
|
||||
var new_m: float = mantissa / other.mantissa
|
||||
var new_e: int = exponent - other.exponent
|
||||
return BigNumber.new(new_m, new_e)
|
||||
|
||||
# ==========================================
|
||||
# COMPARISONS
|
||||
# ==========================================
|
||||
|
||||
## Returns 1 if this > other, -1 if this < other, 0 if equal
|
||||
func compare_to(other: BigNumber) -> int:
|
||||
if mantissa == 0.0 and other.mantissa == 0.0:
|
||||
return 0
|
||||
|
||||
# Handle zero explicitly before sign/exponent checks.
|
||||
if mantissa == 0.0:
|
||||
return -1 if other.mantissa > 0.0 else 1
|
||||
if other.mantissa == 0.0:
|
||||
return 1 if mantissa > 0.0 else -1
|
||||
|
||||
# Handle signs
|
||||
if mantissa > 0 and other.mantissa < 0:
|
||||
return 1
|
||||
if mantissa < 0 and other.mantissa > 0:
|
||||
return -1
|
||||
|
||||
# Both are same sign. Compare exponents first.
|
||||
var sign_mult: int = 1 if mantissa > 0 else -1
|
||||
|
||||
if exponent > other.exponent:
|
||||
return sign_mult
|
||||
if exponent < other.exponent:
|
||||
return -sign_mult
|
||||
|
||||
# Exponents are equal, compare mantissas
|
||||
if mantissa > other.mantissa:
|
||||
return 1
|
||||
if mantissa < other.mantissa:
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
func is_greater_than(other: BigNumber) -> bool:
|
||||
return compare_to(other) == 1
|
||||
|
||||
func is_less_than(other: BigNumber) -> bool:
|
||||
return compare_to(other) == -1
|
||||
|
||||
func is_equal_to(other: BigNumber) -> bool:
|
||||
return compare_to(other) == 0
|
||||
|
||||
## Calculates the progress ratio between this number and a target number.
|
||||
## Returns a standard float clamped between 0.0 and 1.0 for UI progress bars.
|
||||
func get_ratio(target: BigNumber) -> float:
|
||||
if target.mantissa == 0.0:
|
||||
return 1.0 # If the goal is 0, you've already beaten it!
|
||||
if mantissa == 0.0:
|
||||
return 0.0
|
||||
|
||||
var exp_diff: int = exponent - target.exponent
|
||||
|
||||
# If the target is massively larger, progress is practically 0%
|
||||
if exp_diff <= -15:
|
||||
return 0.0
|
||||
|
||||
# If current is equal or greater, progress is 100%
|
||||
if exp_diff >= 15 or is_greater_than(target) or is_equal_to(target):
|
||||
return 1.0
|
||||
|
||||
# Calculate the actual float ratio
|
||||
var ratio: float = (mantissa / target.mantissa) * pow(10.0, float(exp_diff))
|
||||
|
||||
# Clamp it just to be perfectly safe for UI elements
|
||||
return clampf(ratio, 0.0, 1.0)
|
||||
|
||||
# ==========================================
|
||||
# UTILITIES
|
||||
# ==========================================
|
||||
|
||||
## Creates a BigNumber from a standard float or int
|
||||
static func from_float(val: float) -> BigNumber:
|
||||
return BigNumber.new(val, 0)
|
||||
|
||||
## Outputs a UI-friendly string (e.g., "1.50e12")
|
||||
func to_string_sci(decimals: int = 2) -> String:
|
||||
if exponent < 3:
|
||||
# For small numbers, just show the regular number
|
||||
var val: float = mantissa * pow(10.0, float(exponent))
|
||||
return ("%." + str(decimals) + "f") % val
|
||||
|
||||
var format_str: String = "%." + str(decimals) + "f"
|
||||
return (format_str % mantissa) + "e" + str(exponent)
|
||||
|
||||
## Optional: Standard idle game suffix formatting (K, M, B, T, Qa, etc.)
|
||||
func to_string_suffix(decimals: int = 2) -> String:
|
||||
if exponent < 3:
|
||||
return to_string_sci(decimals)
|
||||
|
||||
var suffixes = ["", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc"]
|
||||
var suffix_index: int = floori(exponent / 3.0)
|
||||
|
||||
if suffix_index < suffixes.size():
|
||||
var display_mantissa = mantissa * pow(10.0, float(exponent % 3))
|
||||
var format_str: String = "%." + str(decimals) + "f"
|
||||
return (format_str % display_mantissa) + suffixes[suffix_index]
|
||||
else:
|
||||
# Fall back to scientific if we run out of suffixes
|
||||
return to_string_sci(decimals)
|
||||
|
||||
# ==========================================
|
||||
# SAVE & LOAD (SERIALIZATION)
|
||||
# ==========================================
|
||||
|
||||
## Converts the BigNumber into a basic Dictionary for easy JSON saving.
|
||||
func serialize() -> Dictionary:
|
||||
return {
|
||||
"m": mantissa,
|
||||
"e": exponent
|
||||
}
|
||||
|
||||
## A static factory method that creates a new BigNumber from loaded Dictionary data.
|
||||
static func deserialize(data: Dictionary) -> BigNumber:
|
||||
# Provide fallbacks (0.0 and 0) just in case the save file is corrupted
|
||||
var loaded_m: float = data.get("m", 0.0)
|
||||
var loaded_e: int = data.get("e", 0)
|
||||
return BigNumber.new(loaded_m, loaded_e)
|
||||
Reference in New Issue
Block a user