Last active
April 29, 2024 12:50
-
-
Save rzuf79/7f3b7628533e0b3a140da55186963022 to your computer and use it in GitHub Desktop.
Hnum - a barbaric large number module for games. Stores obscenely large numbers for you and spits out nicely formatted strings for displaying them in a human-readable formak like "1.5k". Supports arithmetic operations and whatnot.
This file contains hidden or 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
--[[ | |
Hnum - a barbaric large number module for games. | |
Usage: | |
local a_hundred = HNum:new(100) | |
print(a_hundred:get_formatted()) -- 100 | |
local a_thousand = HNum:new(1000) | |
print(a_thousand:get_formatted()) -- 1k | |
local also_a_thousand = HNum:new(1, 1) | |
print(also_a_thousand:get_formatted()) -- 1k | |
a_thousand = a_thousand + 1000 | |
print(a_thousand:get_formatted()) -- 2k | |
local a_million = HNum:new(1, 2) | |
print(a_million:get_formatted()) -- 1m | |
local two_billion_from_string = HNum:new("2573684736") | |
print(two_billion_from_string:get_formatted()) -- 2.57b | |
print(two_billion_from_string:to_string()) -- 2573684000 | |
Note, that some precission is lost in the last example. It can be tweaked by increasing the | |
'precision' variable in HNum:from_string(). | |
The methods for converting the number to and from a string are here to make storing the | |
value easily when saving the game. Simply store the :to_string-ed value in your save | |
data and pass it when creating a new instance of HNum during load. | |
]]-- | |
local HNum = {} | |
-- Convert a number to a HNum table if needed | |
local function convert(num) | |
if type(num) == "number" then | |
num = HNum:new(num, 0) | |
end | |
return num | |
end | |
function HNum:new(value, thousands) | |
local newObj = { | |
__type = "hnum", | |
value = value or 0.0, | |
thousands = thousands or 0 | |
} | |
self.__index = self | |
setmetatable(newObj, self) | |
if type(value) == "string" then | |
newObj:from_string(value) | |
end | |
newObj:fix_compression() | |
return newObj | |
end | |
function HNum:to_string() | |
local outText = tostring(self.value) | |
local charsCount = string.len(tostring(math.floor(self.value))) + (self.thousands * 3) | |
local dotIdx = string.find(outText, "%.") | |
if dotIdx ~= nil then | |
outText = string.sub(outText, 1, dotIdx - 1) .. string.sub(outText, dotIdx + 1) | |
end | |
if string.len(outText) > charsCount then | |
outText = string.sub(outText, 1, charsCount) | |
end | |
while string.len(outText) < charsCount do | |
outText = outText .. "0" | |
end | |
return outText | |
end | |
function HNum:from_string(value) | |
local precision = 2 | |
self.thousands = math.floor((string.len(value) - 1) / 3) | |
if self.thousands == 0 then | |
self.value = tonumber(value) | |
self.thousands = 0 | |
end | |
local baseValue = string.sub(value, 1, string.len(value) - (self.thousands * 3)) | |
self.value = tonumber(baseValue) | |
precision = math.min(self.thousands, precision) | |
for i = 0, precision - 1 do | |
local subString = string.sub(value, string.len(baseValue) + (i * 3) + 1, string.len(baseValue) + (i * 3) + 3) | |
self.value = self.value + tonumber(subString) / math.pow(1000.0, i + 1) | |
end | |
end | |
function HNum:fix_compression() | |
while math.abs(self.value) >= 1000.0 or self.thousands < 0 do | |
self.value = self.value / 1000.0 | |
self.thousands = self.thousands + 1 | |
end | |
while math.abs(self.value) < 1.0 and self.thousands > 0 do | |
self.value = self.value * 1000.0 | |
self.thousands = self.thousands - 1 | |
end | |
end | |
function HNum:get_formatted() | |
local outAbbrev = "" | |
local abbrevs = { "", "k", "m", "b", "t" } | |
if self.thousands == 0 then | |
return tostring(math.floor(self.value+0.5)) | |
elseif self.thousands < #abbrevs then | |
outAbbrev = abbrevs[self.thousands + 1] | |
else | |
local firstLetterCharCode = 97 | |
local lastLetterCharCode = 123 | |
local abbrevIdx = self.thousands - #abbrevs | |
outAbbrev = string.char(firstLetterCharCode + abbrevIdx % (lastLetterCharCode - firstLetterCharCode)) | |
abbrevIdx = math.floor(abbrevIdx / (lastLetterCharCode - firstLetterCharCode)) | |
outAbbrev = "" .. string.char(firstLetterCharCode + abbrevIdx % (lastLetterCharCode - firstLetterCharCode)) .. outAbbrev | |
end | |
local truncate_mult = (10 ^ 2) | |
local truncated_val = math.floor(self.value * truncate_mult) /truncate_mult | |
local out = string.format("%.2f", truncated_val) | |
if self.thousands > 0 then | |
out = out:gsub("%.?0*$", "") | |
end | |
return out .. outAbbrev | |
end | |
function HNum:to_float() | |
local outVal = self.value | |
for i = 1, self.thousands do | |
outVal = outVal * 1000 | |
end | |
return outVal | |
end | |
function HNum:is_less_than(other) | |
other = convert(other) | |
return other.thousands > self.thousands or (other.thousands == self.thousands and other.value > self.value) | |
end | |
function HNum:equals(other) | |
other = convert(other) | |
return self.thousands == other.thousands and math.abs(self.value - other.value) < 0.00001 | |
end | |
function HNum.__add(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
HNum.align_values(left_side, right_side) | |
return HNum:new(left_side.value + right_side.value, left_side.thousands) | |
end | |
function HNum.__sub(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
HNum.align_values(left_side, right_side) | |
return HNum:new(left_side.value - right_side.value, left_side.thousands) | |
end | |
function HNum.__mul(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
return HNum:new(left_side.value * right_side.value, left_side.thousands + right_side.thousands) | |
end | |
function HNum.__div(left_side, right_side) | |
left_side = convert(left_side) | |
right_side = convert(right_side) | |
return HNum:new(left_side.value / right_side.value, left_side.thousands - right_side.thousands) | |
end | |
function HNum.__eq(left_side, right_side) | |
return left_side:queals(right_side) | |
end | |
function HNum.__lt(left_side, right_side) -- less than | |
return left_side.is_less_than(right_side) | |
end | |
function HNum.__le(left_side, right_side) -- less or equal | |
return left_side:is_less_than(right_side) or left_side:equals(right_side) | |
end | |
function HNum.align_values(first, second) | |
first = convert(first) | |
second = convert(second) | |
if first.thousands == second.thousands then | |
return | |
end | |
if first.thousands > second.thousands then | |
local diff = first.thousands - second.thousands | |
for i = 1, diff do | |
second.value = second.value / 1000.0 | |
end | |
second.thousands = first.thousands | |
else | |
local diff = second.thousands - first.thousands | |
for i = 1, diff do | |
first.value = first.value / 1000.0 | |
end | |
first.thousands = second.thousands | |
end | |
end | |
return HNum |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment