902 lines
25 KiB
GDScript
902 lines
25 KiB
GDScript
class_name Big
|
|
extends RefCounted
|
|
## Big number class for use in idle / incremental games and other games that needs very large numbers
|
|
##
|
|
## Can format large numbers using a variety of notation methods:[br]
|
|
## AA notation like AA, AB, AC etc.[br]
|
|
## Metric symbol notation k, m, G, T etc.[br]
|
|
## Metric name notation kilo, mega, giga, tera etc.[br]
|
|
## Long names like octo-vigin-tillion or millia-nongen-quin-vigin-tillion (based on work by Landon Curt Noll)[br]
|
|
## Scientic notation like 13e37 or 42e42[br]
|
|
## Long strings like 4200000000 or 13370000000000000000000000000000[br][br]
|
|
## Please note that this class has limited precision and does not fully support negative exponents[br]
|
|
|
|
## Big Number Mantissa
|
|
var mantissa: float
|
|
## Big Number Exponent
|
|
var exponent: int
|
|
|
|
## Metric Symbol Suffixes
|
|
const suffixes_metric_symbol: Dictionary = {
|
|
"0": "",
|
|
"1": "k",
|
|
"2": "M",
|
|
"3": "G",
|
|
"4": "T",
|
|
"5": "P",
|
|
"6": "E",
|
|
"7": "Z",
|
|
"8": "Y",
|
|
"9": "R",
|
|
"10": "Q",
|
|
}
|
|
## Metric Name Suffixes
|
|
const suffixes_metric_name: Dictionary = {
|
|
"0": "",
|
|
"1": "kilo",
|
|
"2": "mega",
|
|
"3": "giga",
|
|
"4": "tera",
|
|
"5": "peta",
|
|
"6": "exa",
|
|
"7": "zetta",
|
|
"8": "yotta",
|
|
"9": "ronna",
|
|
"10": "quetta",
|
|
}
|
|
|
|
# HACK: This dictionary is inefficient, along with toAA().
|
|
# Replace with better, ideally utilizing alphabet_aa system
|
|
## AA Suffixes
|
|
## @deprecated
|
|
static var suffixes_aa: Dictionary = {
|
|
"0": "",
|
|
"1": "k",
|
|
"2": "m",
|
|
"3": "b",
|
|
"4": "t",
|
|
"5": "aa",
|
|
"6": "ab",
|
|
"7": "ac",
|
|
"8": "ad",
|
|
"9": "ae",
|
|
"10": "af",
|
|
"11": "ag",
|
|
"12": "ah",
|
|
"13": "ai",
|
|
"14": "aj",
|
|
"15": "ak",
|
|
"16": "al",
|
|
"17": "am",
|
|
"18": "an",
|
|
"19": "ao",
|
|
"20": "ap",
|
|
"21": "aq",
|
|
"22": "ar",
|
|
"23": "as",
|
|
"24": "at",
|
|
"25": "au",
|
|
"26": "av",
|
|
"27": "aw",
|
|
"28": "ax",
|
|
"29": "ay",
|
|
"30": "az",
|
|
"31": "ba",
|
|
"32": "bb",
|
|
"33": "bc",
|
|
"34": "bd",
|
|
"35": "be",
|
|
"36": "bf",
|
|
"37": "bg",
|
|
"38": "bh",
|
|
"39": "bi",
|
|
"40": "bj",
|
|
"41": "bk",
|
|
"42": "bl",
|
|
"43": "bm",
|
|
"44": "bn",
|
|
"45": "bo",
|
|
"46": "bp",
|
|
"47": "bq",
|
|
"48": "br",
|
|
"49": "bs",
|
|
"50": "bt",
|
|
"51": "bu",
|
|
"52": "bv",
|
|
"53": "bw",
|
|
"54": "bx",
|
|
"55": "by",
|
|
"56": "bz",
|
|
"57": "ca"
|
|
}
|
|
|
|
## AA Alphabet
|
|
const alphabet_aa: Array[String] = [
|
|
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
|
|
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
|
|
]
|
|
|
|
## Latin Ones Prefixes
|
|
const latin_ones: Array[String] = [
|
|
"", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem"
|
|
]
|
|
## Latin Tens Prefixes
|
|
const latin_tens: Array[String] = [
|
|
"", "dec", "vigin", "trigin", "quadragin", "quinquagin", "sexagin", "septuagin", "octogin", "nonagin"
|
|
]
|
|
## Latin Hundreds Prefixes
|
|
const latin_hundreds: Array[String] = [
|
|
"", "cen", "duocen", "trecen", "quadringen", "quingen", "sescen", "septingen", "octingen", "nongen"
|
|
]
|
|
## Latin Special Prefixes
|
|
const latin_special: Array[String] = [
|
|
"", "mi", "bi", "tri", "quadri", "quin", "sex", "sept", "oct", "non"
|
|
]
|
|
|
|
## Various options to control the string presentation of Big Numbers
|
|
static var options = {
|
|
"dynamic_decimals": false,
|
|
"dynamic_numbers": 4,
|
|
"small_decimals": 3,
|
|
"thousand_decimals": 1,
|
|
"big_decimals": 1,
|
|
"scientific_decimals": 3,
|
|
"logarithmic_decimals": 4,
|
|
"thousand_separator": ",",
|
|
"decimal_separator": ".",
|
|
"suffix_separator": "",
|
|
"reading_separator": "",
|
|
"thousand_name": "thousand"
|
|
}
|
|
|
|
## Maximum Big Number Mantissa
|
|
const MANTISSA_MAX: float = 1209600.0
|
|
## Big Number Mantissa floating-point precision
|
|
const MANTISSA_PRECISION: float = 0.0000001
|
|
|
|
## int (signed 64-bit) minimum value
|
|
const INT_MIN: int = -9223372036854775808
|
|
## int (signed 64-bit) maximum value
|
|
const INT_MAX: int = 9223372036854775807
|
|
|
|
func _init(m: Variant = 1.0, e: int = 0) -> void:
|
|
if m is Big:
|
|
mantissa = m.mantissa
|
|
exponent = m.exponent
|
|
elif typeof(m) == TYPE_STRING:
|
|
var scientific: PackedStringArray = m.split("e")
|
|
mantissa = float(scientific[0])
|
|
exponent = int(scientific[1]) if scientific.size() > 1 else 0
|
|
else:
|
|
if typeof(m) != TYPE_INT and typeof(m) != TYPE_FLOAT:
|
|
printerr("Big Error: Unknown data type passed as a mantissa!")
|
|
mantissa = m
|
|
exponent = e
|
|
Big._sizeCheck(mantissa)
|
|
Big.normalize(self)
|
|
|
|
## Verifies (or converts) an argument into a Big number
|
|
static func _typeCheck(n) -> Big:
|
|
if n is Big:
|
|
return n
|
|
var result := Big.new(n)
|
|
return result
|
|
|
|
## Warns if Big number's mantissa exceeds max
|
|
static func _sizeCheck(m: float) -> void:
|
|
if m > MANTISSA_MAX:
|
|
printerr("Big Error: Mantissa \"" + str(m) + "\" exceeds MANTISSA_MAX. Use exponent or scientific notation")
|
|
|
|
|
|
## [url=https://en.wikipedia.org/wiki/Normalized_number]Normalize[/url] a Big number
|
|
static func normalize(big: Big) -> void:
|
|
# Store sign if negative
|
|
var is_negative := false
|
|
if big.mantissa < 0:
|
|
is_negative = true
|
|
big.mantissa *= -1
|
|
|
|
if big.mantissa < 1.0 or big.mantissa >= 10.0:
|
|
var diff: int = floor(log10(big.mantissa))
|
|
if diff > -10 and diff < 248:
|
|
var div = 10.0 ** diff
|
|
if div > MANTISSA_PRECISION:
|
|
big.mantissa /= div
|
|
big.exponent += diff
|
|
while big.exponent < 0:
|
|
big.mantissa *= 0.1
|
|
big.exponent += 1
|
|
while big.mantissa >= 10.0:
|
|
big.mantissa *= 0.1
|
|
big.exponent += 1
|
|
if big.mantissa == 0:
|
|
big.mantissa = 0.0
|
|
big.exponent = 0
|
|
big.mantissa = snapped(big.mantissa, MANTISSA_PRECISION)
|
|
|
|
# Return sign if negative
|
|
if (is_negative):
|
|
big.mantissa *= -1
|
|
|
|
|
|
## Returns the absolute value of a number in Big format
|
|
static func absolute(x) -> Big:
|
|
var result := Big.new(x)
|
|
result.mantissa = abs(result.mantissa)
|
|
return result
|
|
|
|
|
|
## Adds two numbers and returns the Big number result [br][br]
|
|
static func add(x, y) -> Big:
|
|
x = Big._typeCheck(x)
|
|
y = Big._typeCheck(y)
|
|
var result := Big.new(x)
|
|
|
|
var exp_diff: float = y.exponent - x.exponent
|
|
|
|
if exp_diff < 248.0:
|
|
var scaled_mantissa: float = y.mantissa * 10 ** exp_diff
|
|
result.mantissa = x.mantissa + scaled_mantissa
|
|
elif x.isLessThan(y): # When difference between values is too big, discard the smaller number
|
|
result.mantissa = y.mantissa
|
|
result.exponent = y.exponent
|
|
Big.normalize(result)
|
|
return result
|
|
|
|
|
|
## Subtracts two numbers and returns the Big number result
|
|
static func subtract(x, y) -> Big:
|
|
var negated_y := Big.new(-y.mantissa, y.exponent)
|
|
return add(negated_y, x)
|
|
|
|
|
|
## Multiplies two numbers and returns the Big number result
|
|
static func multiply(x, y) -> Big:
|
|
x = Big._typeCheck(x)
|
|
y = Big._typeCheck(y)
|
|
var result := Big.new()
|
|
|
|
var new_exponent: int = y.exponent + x.exponent
|
|
var new_mantissa: float = y.mantissa * x.mantissa
|
|
while new_mantissa >= 10.0:
|
|
new_mantissa /= 10.0
|
|
new_exponent += 1
|
|
result.mantissa = new_mantissa
|
|
result.exponent = new_exponent
|
|
Big.normalize(result)
|
|
return result
|
|
|
|
|
|
## Divides two numbers and returns the Big number result
|
|
static func divide(x, y) -> Big:
|
|
x = Big._typeCheck(x)
|
|
y = Big._typeCheck(y)
|
|
var result := Big.new(x)
|
|
|
|
if y.mantissa > -MANTISSA_PRECISION and y.mantissa < MANTISSA_PRECISION:
|
|
printerr("Big Error: Divide by zero or less than " + str(MANTISSA_PRECISION))
|
|
return x
|
|
var new_exponent = x.exponent - y.exponent
|
|
var new_mantissa = x.mantissa / y.mantissa
|
|
while new_mantissa > 0.0 and new_mantissa < 1.0:
|
|
new_mantissa *= 10.0
|
|
new_exponent -= 1
|
|
result.mantissa = new_mantissa
|
|
result.exponent = new_exponent
|
|
Big.normalize(result)
|
|
return result
|
|
|
|
|
|
## Raises a Big number to the nth power and returns the Big number result
|
|
static func power(x: Big, y) -> Big:
|
|
var result := Big.new(x)
|
|
if typeof(y) == TYPE_INT:
|
|
if y <= 0:
|
|
if y < 0:
|
|
printerr("Big Error: Negative exponents are not supported!")
|
|
result.mantissa = 1.0
|
|
result.exponent = 0
|
|
return result
|
|
|
|
var y_mantissa: float = 1.0
|
|
var y_exponent: int = 0
|
|
|
|
while y > 1:
|
|
Big.normalize(result)
|
|
if y % 2 == 0:
|
|
result.exponent *= 2
|
|
result.mantissa **= 2
|
|
y = y / 2
|
|
else:
|
|
y_mantissa = result.mantissa * y_mantissa
|
|
y_exponent = result.exponent + y_exponent
|
|
result.exponent *= 2
|
|
result.mantissa **= 2
|
|
y = (y - 1) / 2
|
|
|
|
result.exponent = y_exponent + result.exponent
|
|
result.mantissa = y_mantissa * result.mantissa
|
|
Big.normalize(result)
|
|
return result
|
|
elif typeof(y) == TYPE_FLOAT:
|
|
if result.mantissa == 0:
|
|
return result
|
|
|
|
# fast track
|
|
var temp: float = result.exponent * y
|
|
var newMantissa = result.mantissa ** y
|
|
if (round(y) == y
|
|
and temp <= INT_MAX
|
|
and temp >= INT_MIN
|
|
and is_finite(temp)
|
|
):
|
|
if is_finite(newMantissa):
|
|
result.mantissa = newMantissa
|
|
result.exponent = int(temp)
|
|
Big.normalize(result)
|
|
return result
|
|
|
|
# a bit slower, still supports floats
|
|
var newExponent: int = int(temp)
|
|
var residue: float = temp - newExponent
|
|
newMantissa = 10 ** (y * Big.log10(result.mantissa) + residue)
|
|
if newMantissa != INF and newMantissa != -INF:
|
|
result.mantissa = newMantissa
|
|
result.exponent = newExponent
|
|
Big.normalize(result)
|
|
return result
|
|
|
|
if round(y) != y:
|
|
printerr("Big Error: Power function does not support large floats, use integers!")
|
|
|
|
return power(x, int(y))
|
|
else:
|
|
printerr("Big Error: Unknown/unsupported data type passed as an exponent in power function!")
|
|
return x
|
|
|
|
|
|
## Square Roots a given Big number and returns the Big number result
|
|
static func root(x: Big) -> Big:
|
|
var result := Big.new(x)
|
|
|
|
if result.exponent % 2 == 0:
|
|
result.mantissa = sqrt(result.mantissa)
|
|
@warning_ignore("integer_division")
|
|
result.exponent = result.exponent / 2
|
|
else:
|
|
result.mantissa = sqrt(result.mantissa * 10)
|
|
@warning_ignore("integer_division")
|
|
result.exponent = (result.exponent - 1) / 2
|
|
Big.normalize(result)
|
|
return result
|
|
|
|
|
|
## Modulos a number and returns the Big number result
|
|
static func modulo(x, y) -> Big:
|
|
var result := Big.new(x.mantissa, x.exponent)
|
|
y = Big._typeCheck(y)
|
|
var big = { "mantissa": x.mantissa, "exponent": x.exponent }
|
|
Big.divide(result, y)
|
|
Big.roundDown(result)
|
|
Big.multiply(result, y)
|
|
Big.subtract(result, big)
|
|
result.mantissa = abs(result.mantissa)
|
|
return result
|
|
|
|
|
|
## Rounds down a Big number
|
|
static func roundDown(x: Big) -> Big:
|
|
if x.exponent == 0:
|
|
x.mantissa = floor(x.mantissa)
|
|
else:
|
|
var precision := 1.0
|
|
for i in range(min(8, x.exponent)):
|
|
precision /= 10.0
|
|
if precision < MANTISSA_PRECISION:
|
|
precision = MANTISSA_PRECISION
|
|
x.mantissa = floor(x.mantissa / precision) * precision
|
|
return x
|
|
|
|
|
|
## Equivalent of [code]min(Big, Big)[/code]
|
|
static func minValue(m, n) -> Big:
|
|
m = Big._typeCheck(m)
|
|
if m.isLessThan(n):
|
|
return m
|
|
else:
|
|
return n
|
|
|
|
|
|
## Equivalent of [code]max(Big, Big)[/code]
|
|
static func maxValue(m, n) -> Big:
|
|
m = Big._typeCheck(m)
|
|
if m.isGreaterThan(n):
|
|
return m
|
|
else:
|
|
return n
|
|
|
|
|
|
## Equivalent of [code]Big + n[/code]
|
|
func plus(n) -> Big:
|
|
return Big.add(self, n)
|
|
|
|
|
|
## Equivalent of [code]Big += n[/code]
|
|
func plusEquals(n) -> Big:
|
|
var new_value = Big.add(self, n)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
## Equivalent of [code]Big - n[/code]
|
|
func minus(n) -> Big:
|
|
return Big.subtract(self, n)
|
|
|
|
|
|
## Equivalent of [code]Big -= n[/code]
|
|
func minusEquals(n) -> Big:
|
|
var new_value: Big = Big.subtract(self, n)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
|
|
## Equivalent of [code]Big * n[/code]
|
|
func times(n) -> Big:
|
|
return Big.multiply(self, n)
|
|
|
|
|
|
## Equivalent of [code]Big *= n[/code]
|
|
func timesEquals(n) -> Big:
|
|
var new_value: Big = Big.multiply(self, n)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
|
|
## Equivalent of [code]Big / n[/code]
|
|
func dividedBy(n) -> Big:
|
|
return Big.divide(self, n)
|
|
|
|
|
|
## Equivalent of [code]Big /= n[/code]
|
|
func dividedByEquals(n) -> Big:
|
|
var new_value: Big = Big.divide(self, n)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
|
|
## Equivalent of [code]Big % n[/code]
|
|
func mod(n) -> Big:
|
|
return Big.modulo(self, n)
|
|
|
|
|
|
## Equivalent of [code]Big %= n[/code]
|
|
func modEquals(n) -> Big:
|
|
var new_value := Big.modulo(self, n)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
|
|
## Equivalent of [code]Big ** n[/code]
|
|
func toThePowerOf(n) -> Big:
|
|
return Big.power(self, n)
|
|
|
|
|
|
## Equivalent of [code]Big **= n[/code]
|
|
func toThePowerOfEquals(n) -> Big:
|
|
var new_value: Big = Big.power(self, n)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
|
|
## Equivalent of [code]sqrt(Big)[/code]
|
|
func squareRoot() -> Big:
|
|
var new_value := Big.root(self)
|
|
mantissa = new_value.mantissa
|
|
exponent = new_value.exponent
|
|
return self
|
|
|
|
|
|
## Equivalent of [code]Big == n[/code]
|
|
func isEqualTo(n) -> bool:
|
|
n = Big._typeCheck(n)
|
|
Big.normalize(n)
|
|
return n.exponent == exponent and is_equal_approx(n.mantissa, mantissa)
|
|
|
|
|
|
## Equivalent of [code]Big > n[/code]
|
|
func isGreaterThan(n) -> bool:
|
|
return !isLessThanOrEqualTo(n)
|
|
|
|
|
|
## Equivalent of [code]Big >== n[/code]
|
|
func isGreaterThanOrEqualTo(n) -> bool:
|
|
return !isLessThan(n)
|
|
|
|
|
|
## Equivalent of [code]Big < n[/code]
|
|
func isLessThan(n) -> bool:
|
|
n = Big._typeCheck(n)
|
|
Big.normalize(n)
|
|
if (mantissa == 0
|
|
and (n.mantissa > MANTISSA_PRECISION or mantissa < MANTISSA_PRECISION)
|
|
and n.mantissa == 0
|
|
):
|
|
return false
|
|
if exponent < n.exponent:
|
|
if exponent == n.exponent - 1 and mantissa > 10*n.mantissa:
|
|
return false #9*10^3 > 0.1*10^4
|
|
return true
|
|
elif exponent == n.exponent:
|
|
if mantissa < n.mantissa:
|
|
return true
|
|
return false
|
|
else:
|
|
if exponent == n.exponent + 1 and mantissa * 10 < n.mantissa:
|
|
return true
|
|
return false
|
|
|
|
|
|
## Equivalent of [code]Big <= n[/code]
|
|
func isLessThanOrEqualTo(n) -> bool:
|
|
n = Big._typeCheck(n)
|
|
Big.normalize(n)
|
|
if isLessThan(n):
|
|
return true
|
|
if n.exponent == exponent and is_equal_approx(n.mantissa, mantissa):
|
|
return true
|
|
return false
|
|
|
|
|
|
static func log10(x) -> float:
|
|
return log(x) * 0.4342944819032518
|
|
|
|
|
|
func absLog10() -> float:
|
|
return exponent + Big.log10(abs(mantissa))
|
|
|
|
|
|
func ln() -> float:
|
|
return 2.302585092994045 * logN(10)
|
|
|
|
|
|
func logN(base) -> float:
|
|
return (2.302585092994046 / log(base)) * (exponent + Big.log10(mantissa))
|
|
|
|
|
|
func pow10(value: int) -> void:
|
|
mantissa = 10 ** (value % 1)
|
|
exponent = int(value)
|
|
|
|
|
|
## Sets the Thousand name option
|
|
static func setThousandName(name: String) -> void:
|
|
options.thousand_name = name
|
|
|
|
|
|
## Sets the Thousand Separator option
|
|
static func setThousandSeparator(separator: String) -> void:
|
|
options.thousand_separator = separator
|
|
|
|
|
|
## Sets the Decimal Separator option
|
|
static func setDecimalSeparator(separator: String) -> void:
|
|
options.decimal_separator = separator
|
|
|
|
|
|
## Sets the Suffix Separator option
|
|
static func setSuffixSeparator(separator: String) -> void:
|
|
options.suffix_separator = separator
|
|
|
|
|
|
## Sets the Reading Separator option
|
|
static func setReadingSeparator(separator: String) -> void:
|
|
options.reading_separator = separator
|
|
|
|
|
|
## Sets the Dynamic Decimals option
|
|
static func setDynamicDecimals(d: bool) -> void:
|
|
options.dynamic_decimals = d
|
|
|
|
|
|
## Sets the Dynamic numbers digits option
|
|
static func setDynamicNumbers(d: int) -> void:
|
|
options.dynamic_numbers = d
|
|
|
|
|
|
## Sets the small decimal digits option
|
|
static func setSmallDecimals(d: int) -> void:
|
|
options.small_decimals = d
|
|
|
|
|
|
## Sets the thousand decimal digits option
|
|
static func setThousandDecimals(d: int) -> void:
|
|
options.thousand_decimals = d
|
|
|
|
|
|
## Sets the big decimal digits option
|
|
static func setBigDecimals(d: int) -> void:
|
|
options.big_decimals = d
|
|
|
|
|
|
## Sets the scientific notation decimal digits option
|
|
static func setScientificDecimals(d: int) -> void:
|
|
options.scientific_decimals = d
|
|
|
|
|
|
## Sets the logarithmic notation decimal digits option
|
|
static func setLogarithmicDecimals(d: int) -> void:
|
|
options.logarithmic_decimals = d
|
|
|
|
|
|
## Converts the Big Number into a string
|
|
func toString() -> String:
|
|
var mantissa_decimals := 0
|
|
if str(mantissa).find(".") >= 0:
|
|
mantissa_decimals = str(mantissa).split(".")[1].length()
|
|
if mantissa_decimals > exponent:
|
|
if exponent < 248:
|
|
return str(mantissa * 10 ** exponent)
|
|
else:
|
|
return toPlainScientific()
|
|
else:
|
|
var mantissa_string := str(mantissa).replace(".", "")
|
|
for _i in range(exponent-mantissa_decimals):
|
|
mantissa_string += "0"
|
|
return mantissa_string
|
|
|
|
|
|
## Converts the Big Number into a string (in plain Scientific format)
|
|
func toPlainScientific() -> String:
|
|
return str(mantissa) + "e" + str(exponent)
|
|
|
|
|
|
## Converts the Big Number into a string (in Scientific format)
|
|
func toScientific(no_decimals_on_small_values = false, force_decimals = false) -> String:
|
|
if exponent < 3:
|
|
var decimal_increments: float = 1 / (10 ** options.scientific_decimals / 10)
|
|
var value := str(snappedf(mantissa * 10 ** exponent, decimal_increments))
|
|
var split := value.split(".")
|
|
if no_decimals_on_small_values:
|
|
return split[0]
|
|
if split.size() > 1:
|
|
for i in range(options.logarithmic_decimals):
|
|
if split[1].length() < options.scientific_decimals:
|
|
split[1] += "0"
|
|
return split[0] + options.decimal_separator + split[1].substr(0,min(options.scientific_decimals, options.dynamic_numbers - split[0].length() if options.dynamic_decimals else options.scientific_decimals))
|
|
else:
|
|
return value
|
|
else:
|
|
var split := str(mantissa).split(".")
|
|
if split.size() == 1:
|
|
split.append("")
|
|
if force_decimals:
|
|
for i in range(options.scientific_decimals):
|
|
if split[1].length() < options.scientific_decimals:
|
|
split[1] += "0"
|
|
return split[0] + options.decimal_separator + split[1].substr(0,min(options.scientific_decimals, options.dynamic_numbers-1 - str(exponent).length() if options.dynamic_decimals else options.scientific_decimals)) + "e" + str(exponent)
|
|
|
|
|
|
## Converts the Big Number into a string (in Logarithmic format)
|
|
func toLogarithmic(no_decimals_on_small_values = false) -> String:
|
|
var decimal_increments: float = 1 / (10 ** options.logarithmic_decimals / 10)
|
|
if exponent < 3:
|
|
var value := str(snappedf(mantissa * 10 ** exponent, decimal_increments))
|
|
var split := value.split(".")
|
|
if no_decimals_on_small_values:
|
|
return split[0]
|
|
if split.size() > 1:
|
|
for i in range(options.logarithmic_decimals):
|
|
if split[1].length() < options.logarithmic_decimals:
|
|
split[1] += "0"
|
|
return split[0] + options.decimal_separator + split[1].substr(0,min(options.logarithmic_decimals, options.dynamic_numbers - split[0].length() if options.dynamic_decimals else options.logarithmic_decimals))
|
|
else:
|
|
return value
|
|
var dec := str(snappedf(abs(log(mantissa) / log(10) * 10), decimal_increments))
|
|
dec = dec.replace(".", "")
|
|
for i in range(options.logarithmic_decimals):
|
|
if dec.length() < options.logarithmic_decimals:
|
|
dec += "0"
|
|
var formated_exponent := formatExponent(exponent)
|
|
dec = dec.substr(0, min(options.logarithmic_decimals, options.dynamic_numbers - formated_exponent.length() if options.dynamic_decimals else options.logarithmic_decimals))
|
|
return "e" + formated_exponent + options.decimal_separator + dec
|
|
|
|
|
|
## Formats an exponent for string format
|
|
func formatExponent(value) -> String:
|
|
if value < 1000:
|
|
return str(value)
|
|
var string := str(value)
|
|
var string_mod := string.length() % 3
|
|
var output := ""
|
|
for i in range(0, string.length()):
|
|
if i != 0 and i % 3 == string_mod:
|
|
output += options.thousand_separator
|
|
output += string[i]
|
|
return output
|
|
|
|
|
|
## Converts the Big Number into a float
|
|
func toFloat() -> float:
|
|
return snappedf(float(str(mantissa) + "e" + str(exponent)),0.01)
|
|
|
|
|
|
func toPrefix(no_decimals_on_small_values = false, use_thousand_symbol=true, force_decimals=true, scientic_prefix=false) -> String:
|
|
var number: float = mantissa
|
|
if not scientic_prefix:
|
|
var hundreds = 1
|
|
for _i in range(exponent % 3):
|
|
hundreds *= 10
|
|
number *= hundreds
|
|
|
|
var split := str(number).split(".")
|
|
if split.size() == 1:
|
|
split.append("")
|
|
if force_decimals:
|
|
var max_decimals = max(max(options.small_decimals, options.thousand_decimals), options.big_decimals)
|
|
for i in range(max_decimals):
|
|
if split[1].length() < max_decimals:
|
|
split[1] += "0"
|
|
|
|
if no_decimals_on_small_values and exponent < 3:
|
|
return split[0]
|
|
elif exponent < 3:
|
|
if options.small_decimals == 0 or split[1] == "":
|
|
return split[0]
|
|
else:
|
|
return split[0] + options.decimal_separator + split[1].substr(0,min(options.small_decimals, options.dynamic_numbers - split[0].length() if options.dynamic_decimals else options.small_decimals))
|
|
elif exponent < 6:
|
|
if options.thousand_decimals == 0 or (split[1] == "" and use_thousand_symbol):
|
|
return split[0]
|
|
else:
|
|
if use_thousand_symbol: # when the prefix is supposed to be using with a K for thousand
|
|
for i in range(3):
|
|
if split[1].length() < 3:
|
|
split[1] += "0"
|
|
return split[0] + options.decimal_separator + split[1].substr(0,min(3, options.dynamic_numbers - split[0].length() if options.dynamic_decimals else 3))
|
|
else:
|
|
for i in range(3):
|
|
if split[1].length() < 3:
|
|
split[1] += "0"
|
|
return split[0] + options.thousand_separator + split[1].substr(0,3)
|
|
else:
|
|
if options.big_decimals == 0 or split[1] == "":
|
|
return split[0]
|
|
else:
|
|
return split[0] + options.decimal_separator + split[1].substr(0,min(options.big_decimals, options.dynamic_numbers - split[0].length() if options.dynamic_decimals else options.big_decimals))
|
|
|
|
|
|
func _latinPower(european_system) -> int:
|
|
if european_system:
|
|
@warning_ignore("integer_division")
|
|
return int(exponent / 3) / 2
|
|
@warning_ignore("integer_division")
|
|
return int(exponent / 3) - 1
|
|
|
|
|
|
func _latinPrefix(european_system) -> String:
|
|
var ones := _latinPower(european_system) % 10
|
|
var tens := int(_latinPower(european_system) / floor(10)) % 10
|
|
@warning_ignore("integer_division")
|
|
var hundreds := int(_latinPower(european_system) / 100) % 10
|
|
@warning_ignore("integer_division")
|
|
var millias := int(_latinPower(european_system) / 1000) % 10
|
|
|
|
var prefix := ""
|
|
if _latinPower(european_system) < 10:
|
|
prefix = latin_special[ones] + options.reading_separator + latin_tens[tens] + options.reading_separator + latin_hundreds[hundreds]
|
|
else:
|
|
prefix = latin_hundreds[hundreds] + options.reading_separator + latin_ones[ones] + options.reading_separator + latin_tens[tens]
|
|
|
|
for _i in range(millias):
|
|
prefix = "millia" + options.reading_separator + prefix
|
|
|
|
return prefix.lstrip(options.reading_separator).rstrip(options.reading_separator)
|
|
|
|
|
|
func _tillionOrIllion(european_system) -> String:
|
|
if exponent < 6:
|
|
return ""
|
|
var powerKilo := _latinPower(european_system) % 1000
|
|
if powerKilo < 5 and powerKilo > 0 and _latinPower(european_system) < 1000:
|
|
return ""
|
|
if (
|
|
powerKilo >= 7 and powerKilo <= 10
|
|
or int(powerKilo / floor(10)) % 10 == 1
|
|
):
|
|
return "i"
|
|
return "ti"
|
|
|
|
|
|
func _llionOrLliard(european_system) -> String:
|
|
if exponent < 6:
|
|
return ""
|
|
if int(exponent/floor(3)) % 2 == 1 and european_system:
|
|
return "lliard"
|
|
return "llion"
|
|
|
|
|
|
func getLongName(european_system = false, prefix="") -> String:
|
|
if exponent < 6:
|
|
return ""
|
|
else:
|
|
return prefix + _latinPrefix(european_system) + options.reading_separator + _tillionOrIllion(european_system) + _llionOrLliard(european_system)
|
|
|
|
|
|
## Converts the Big Number into a string (in American Long Name format)
|
|
func toAmericanName(no_decimals_on_small_values = false) -> String:
|
|
return toLongName(no_decimals_on_small_values, false)
|
|
|
|
|
|
## Converts the Big Number into a string (in European Long Name format)
|
|
func toEuropeanName(no_decimals_on_small_values = false) -> String:
|
|
return toLongName(no_decimals_on_small_values, true)
|
|
|
|
|
|
## Converts the Big Number into a string (in Latin Long Name format)
|
|
func toLongName(no_decimals_on_small_values = false, european_system = false) -> String:
|
|
if exponent < 6:
|
|
if exponent > 2:
|
|
return toPrefix(no_decimals_on_small_values) + options.suffix_separator + options.thousand_name
|
|
else:
|
|
return toPrefix(no_decimals_on_small_values)
|
|
|
|
var suffix = _latinPrefix(european_system) + options.reading_separator + _tillionOrIllion(european_system) + _llionOrLliard(european_system)
|
|
|
|
return toPrefix(no_decimals_on_small_values) + options.suffix_separator + suffix
|
|
|
|
|
|
## Converts the Big Number into a string (in Metric Symbols format)
|
|
func toMetricSymbol(no_decimals_on_small_values = false) -> String:
|
|
@warning_ignore("integer_division")
|
|
var target := int(exponent / 3)
|
|
|
|
if not suffixes_metric_symbol.has(str(target)):
|
|
return toScientific()
|
|
else:
|
|
return toPrefix(no_decimals_on_small_values) + options.suffix_separator + suffixes_metric_symbol[str(target)]
|
|
|
|
|
|
## Converts the Big Number into a string (in Metric Name format)
|
|
func toMetricName(no_decimals_on_small_values = false) -> String:
|
|
@warning_ignore("integer_division")
|
|
var target := int(exponent / 3)
|
|
|
|
if not suffixes_metric_name.has(str(target)):
|
|
return toScientific()
|
|
else:
|
|
return toPrefix(no_decimals_on_small_values) + options.suffix_separator + suffixes_metric_name[str(target)]
|
|
|
|
# HACK: This function is wasteful and requires remaking.
|
|
# It adds new entries to the dictionary suffixes_aa instead of generating the suffixes
|
|
# like the other functions
|
|
## Converts the Big Number into a string (in AA format)
|
|
## @deprecated
|
|
func toAA(no_decimals_on_small_values = false, use_thousand_symbol = true, force_decimals=false) -> String:
|
|
@warning_ignore("integer_division")
|
|
var target := int(exponent / 3)
|
|
var aa_index := str(target)
|
|
var suffix := ""
|
|
|
|
if not suffixes_aa.has(aa_index):
|
|
var offset := target + 22
|
|
var base := alphabet_aa.size()
|
|
while offset > 0:
|
|
offset -= 1
|
|
var digit := offset % base
|
|
suffix = alphabet_aa[digit] + suffix
|
|
offset /= base
|
|
suffixes_aa[aa_index] = suffix
|
|
else:
|
|
suffix = suffixes_aa[aa_index]
|
|
|
|
if not use_thousand_symbol and target == 1:
|
|
suffix = ""
|
|
|
|
var prefix = toPrefix(no_decimals_on_small_values, use_thousand_symbol, force_decimals)
|
|
|
|
return prefix + options.suffix_separator + suffix
|