Overview
Roblox’s default number type is based on double precision floating-point (64-bit), which limits you to roughly 1e+308 before the number overflows into infinity. This limitation can cause serious issues when your game involves currencies, stats, or progression systems that grow exponentially over time.
The BigNumber module provides a custom arithmetic system that allows you to represent and calculate values far beyond Roblox’s numeric boundaries.
It uses a mantissa + exponent structure similar to scientific notation (for example, 1.23e+100), ensuring your numbers remain mathematically valid even at extreme magnitudes.
This module focuses on stability, readability, and usability, and includes automatic normalization, string parsing, abbreviations, and compact formatting.
How It Works
For example, BigNumber.new(5.2, 6) is equal to 5.2 × 10^6 or 5,200,000.
The module ensures that:
- The mantissa always remains between 1 and 10.
- The exponent scales accordingly.
- Trailing decimals and zeros are trimmed automatically to keep results clean.
This process is called normalization, and it ensures consistency across all calculations. Even if you multiply, divide, or raise to a power, the output will always remain properly normalized.
When NOT to Use It
While BigNumber is powerful, it is not a replacement for all numeric needs.
Here are the main limitations and things you should avoid:
- Do not use
NumberValueinstances to store BigNumber results.
Roblox’sNumberValuecannot store custom objects, and even if you store the mantissa or exponent separately, it will overflow once the exponent becomes too large.
BigNumber exists specifically because Roblox’s number type cannot handle such scales. - Not designed for frequent per-frame operations.
While efficient, BigNumber is written in pure Lua and does not match native arithmetic speed. Avoid using it inside physics or rendering loops. - No direct comparison to standard numbers without conversion.
You must use provided methods (compare,toString, orfrom) to interact with regular numeric values. - This module does not support negative exponents for abbreviations.
It handles positive exponents perfectly, but for very small decimals (less than 1), the display system switches to a scientific format automatically.
Note: code development is still open
--// BigNumber Module
--// Author: fariy's
--// Purpose: Allows calculations far beyond Roblox’s numeric limits using mantissa + exponent notation.
--[[
Example :
local a = BigNumber.new(3, 5)
local b = BigNumber.new(7, 5)
local sum = a:add(b)
print(sum:toString()) -- "1M"
--]]
local BigNumber = {}
BigNumber.__index = BigNumber
local TrimDecimalLevel = 4;
local Large_enough_to_shorten = 5; -- Determines how many digits the number must have in order to apply abbreviations.
local decimal_for_small_numbers = true; -- like 100'000
local Abbreviations = require(script:WaitForChild("Abbreviations"));
-- // Helper for trimming zeros
function BigNumber.TrimDecimal(num:number, IWantNumber:boolean?): number
local multiplier = 10 ^ TrimDecimalLevel
local trimmed = math.floor(num * multiplier) / multiplier
-- Convert the number to a string and remove unnecessary zeros
local str = string.format("%." .. TrimDecimalLevel .. "f", trimmed)
str = str:gsub("0+$", "") -- remove trailing zeros
str = str:gsub("%.$", "") -- if the last character is ‘.’, delete it too
if IWantNumber then
return tonumber(str) or 0;
end;
return str;
end;
-- // Constructor
function BigNumber.new(mantissa:number, exponent:number)
local self = setmetatable({}, BigNumber)
self.m = mantissa or 0
self.e = exponent or 0
self:normalize()
return self
end
-- // Normalize (ensures mantissa is always < 10)
function BigNumber:normalize()
-- Set the mantissa to the range 1–10
while math.abs(self.m) >= 10 do
self.m /= 10
self.e += 1
end
while math.abs(self.m) < 1 and self.m ~= 0 do
self.m *= 10
self.e -= 1
end
-- Rounding: 3 decimal places
self.m = BigNumber.TrimDecimal(self.m);
-- If the mantissa is, for example, 1.000 → 1
local mantissaStr = tostring(self.m)
self.m = tonumber(mantissaStr)
-- The same process for the exponent (rare but for stability)
self.e = BigNumber.TrimDecimal(self.e);
local expStr = tostring(self.e)
self.e = tonumber(expStr) or 0;
end
-- // Clone
function BigNumber:clone()
return BigNumber.new(self.m, self.e)
end
-- // Comparison (returns -1, 0, 1)
function BigNumber:compare(other)
if self.e == other.e then
if self.m == other.m then
return 0
elseif self.m > other.m then
return 1
else
return -1
end
elseif self.e > other.e then
return 1
else
return -1
end
end
-- // Addition
function BigNumber:add(other)
local a = self:clone()
local b = other:clone()
if math.abs(a.e - b.e) > 20 then
-- Exponents are too far apart; smaller number is negligible
return (a.e > b.e) and a or b
end
if a.e > b.e then
b.m = b.m * 10^(b.e - a.e)
b.e = a.e
elseif b.e > a.e then
a.m = a.m * 10^(a.e - b.e)
a.e = b.e
end
local result = BigNumber.new(a.m + b.m, a.e)
result:normalize()
return result
end
-- // Subtraction
function BigNumber:sub(other)
local negOther = BigNumber.new(-other.m, other.e)
return self:add(negOther)
end
-- // Multiplication
function BigNumber:mul(other)
local result = BigNumber.new(self.m * other.m, self.e + other.e)
result:normalize()
return result
end
-- // Division
function BigNumber:div(other)
local result = BigNumber.new(self.m / other.m, self.e - other.e)
result:normalize()
return result
end
-- // Pow
function BigNumber:pow(power:number)
if power == 0 then
return BigNumber.new(1, 0)
end
if self.m == 0 then
return BigNumber.new(0, 0)
end
-- The mantissa is taken, the exponent is multiplied
local newMantissa = self.m ^ power
local newExponent = self.e * power
local result = BigNumber.new(newMantissa, newExponent)
result:normalize()
return result
end
-- // Convert to String (for printing)
function BigNumber:toString()
-- Small numbers (below 10^6)
if self.e < Large_enough_to_shorten then
local num = self.m * 10 ^ self.e
local str = BigNumber.TrimDecimal(num);
if decimal_for_small_numbers then
-- Flip the number over and divide it into groups of three, then flip it back over. Like my dad
local formatted = string.reverse(str):gsub("(%d%d%d)", "%1'");
-- If there is an unnecessary ' at the end, remove it.
formatted = string.reverse(formatted):gsub("^'", "");
return formatted
end;
return str
end
-- Abbreviation system
for i = #Abbreviations, 1, -1 do
local data = Abbreviations[i]
if self.e >= data.Power then
local scaled = BigNumber.new(self.m, self.e - data.Power);
if not scaled.e or not scaled.m then error("attempt to perform arithmetic (pow) on number and nil") end;
local num = scaled.m * (10 ^ scaled.e);
-- If it's too big, switch to "e+" format
if i == #Abbreviations and self.e - data.Power > 3 then
local formatted = BigNumber.TrimDecimal(self.m)
return formatted .. "e+" .. tostring(self.e):gsub("%.?0+$", "")
end
local formatted = BigNumber.TrimDecimal(num)
return formatted .. data.Suffix
end
end
-- Fallback: Scientific format (like 1e+30)
local formatted = BigNumber.TrimDecimal(self.m)
return formatted .. "e+" .. tostring(self.e):gsub("%.?0+$", "")
end
-- // Utility: Create from number or BigNumber
function BigNumber.from(value:any)
if typeof(value) == "table" and value.m and value.e then
return BigNumber.new(value.m, value.e)
elseif typeof(value) == "number" then
local e = 0
local m = value
if m == 0 then
return BigNumber.new(0, 0)
end
while math.abs(m) >= 10 do
m /= 10
e += 1
end
while math.abs(m) < 1 do
m *= 10
e -= 1
end
return BigNumber.new(m, e)
else
error("Invalid value for BigNumber.from")
end
end
function BigNumber.parse(str:string)
-- Trim gaps
str = tostring(str);
str = string.gsub(str, "%s+", "")
-- Scientific notation control (e.g., 1.23e+45)
local mantissa, exponent = string.match(str, "([%d%.%-]+)e%+?(%-?%d+)")
if mantissa and exponent then
return BigNumber.new(tonumber(mantissa), tonumber(exponent))
end
-- Abbreviation (e.g., 9.87Dc)
for _, data in ipairs(Abbreviations) do
-- Let's ignore the case difference
if string.lower(str):find(string.lower(data.Suffix) .. "$") then
local num = string.gsub(str, data.Suffix, "")
return BigNumber.new(tonumber(num), data.Power)
end
end
-- Normal number (e.g., “12345”)
if tonumber(str) then
local num = tonumber(str)
return BigNumber.from(num)
end
error("BigNumber.parse: Invalid number format -> " .. tostring(str))
end
-- // Easy Operations Helper
-- // Example: BigNumber.easy("1e+50") + BigNumber.easy("5e+40")
function BigNumber.easy(value)
value = tostring(value);
local mantissa, exponent = string.match(value, "([%d%.]+)e%+?(%-?%d+)")
if mantissa and exponent then
return BigNumber.new(tonumber(mantissa), tonumber(exponent))
end
error("Invalid BigNumber string format: " .. tostring(value))
end
-- // Quick String Formatter
-- // Converts a raw number directly into abbreviated string (e.g., 100000 -> "100K")
function BigNumber.easyString(value:number): string
local exponent = math.floor(math.log10(value));
local mantissa = value / (10 ^ exponent);
local data = BigNumber.new(mantissa, exponent);
return data:toString();
end
return BigNumber