[2.0.1] BigInteger, safely store and represent values over 2⁵³

BigInteger version 2.0.0
What has changed in version 1:

  • More memory efficient as they’re now garbage collected when they’re not used
  • < and > sign fixed for negative numbers
  • Log slighty more accurate
  • A little bit of optimisation
  • Lowercased method name

About

BigInteger is a moudle that allows you to safely store integers above 2⁵³ (2 ** 53) without precision loss. It can thereotically store up to 2 ** 2 ** 53

Why you may ask?

Luau number’s uses the double precision floating point format as specified by IEEE
It’s meant to fix the precision loss for doubles above 9007 billion (9.007.199.254.740.992).

Constructor

BigInteger BigInteger.new(object value)
Create a BigInteger based off the value

Values

BigInteger BigInteger.zero
BigInteger BigInteger.one
BigInteger BigInteger.minusone
Should be self explainatory, BigInteger.zero = BigInteger.new(0), BigInteger.one = BigInteger.new(1), BigInteger.minusone = BigInteger.new(-1).

Class methods

BigInteger BigInteger.abs(BigInteger value)
Return the absolute value of the BigInteger.

BigInteger.new('-78259438907245387524387245378902543890752438907'):abs() --> 78259438907245387524387245378902543890752438907

BigInteger BigInteger.band(BigInteger left, BigInteger right)
Bitwise ‘and’ for the two BigInteger value

BigInteger BigInteger.bor(BigInteger left, BigInteger right)
Bitwise ‘or’ for the two BigInteger value

BigInteger BigInteger.bnot(BigInteger left, BigInteger right)
Bitwise ‘not’ for the two BigInteger value

BigInteger BigInteger.bxor(BigInteger left, BigInteger right)
Bitwise ‘xor’ for the two BigInteger value

BigInteger BigInteger.shl(BigInteger value, double disp)
Left shift fo the BigInteger value, basically doing value << disp

BigInteger BigInteger.shr(BigInteger value, double disp)
Right shift fo the BigInteger value, basically doing value >> disp

BigInteger BigInteger BigInteger.divrem(BigInteger left, BigInteger right)
Dividing and getting the remainder at the same time, basically

left / right, left % right

double BigInteger.log(BigInteger value, double base = 2.718281828459045)
Logarithm of the BigInteger value, the default for base is Euler’s number

double BigInteger.log2(BigInteger value)
Sugar syntax for BigInteger.log(value, 2)

double BigInteger.log8(BigInteger value)
Sugar syntax for BigInteger.log(value, 8)

double BigInteger.log10(BigInteger value)
Sugar syntax for BigInteger.log(value, 10)

double BigInteger.log12(BigInteger value)
Sugar syntax for BigInteger.log(value, 12)

double BigInteger.log16(BigInteger value)
Sugar syntax for BigInteger.log(value, 16)

boolean BigInteger.iseven(BigInteger value)
Reuturns a boolean that determines whether the BigInteger value is even or not.

boolean BigInteger.ispoweroftwo(BigInteger value)
Returns a boolean that determines whether the BigInteger value is power of 2 or not.

BigInteger BigInteger.max(BigInteger ···)
Gets the highest value out of all the arguments

BigInteger BigInteger.min(BigInteger ···)
Gets the lowest value out of all the arguments

BigInteger BigInteger.sum(BigInteger ···)
Returns the sum of the value when adding all the argument

BigInteger BigInteger.clamp(BigInteger start, BigInteger min, BigInteger max)
Clamps the number between and double

int BigInteger.sign(BigInteger value)
Get the sign of the BigInteger value

Value Sign
< 0 -1
= 0 0

0 | 1

BigInteger BigInteger.copysign(BigInteger value, BigInteger sign)
Return the BigInteger value argument but with the sign of the sign argument. (0 will be treated as positive)

BigInteger.copysign(BigInteger.new('8975267892567892458379879524387945289734523'), BigInteger.new('549420380923498023490834289034290834289043298098234')) --> 8975267892567892458379879524387945289734523
BigInteger.copysign(BigInteger.new('8359408903459083459084538909034580894359805'), BigInteger.new('-389052890459803954945803890453980543980')) --> -8359408903459083459084538909034580894359805
BigInteger.copysign(BigInteger.new('-2398408903429083428902348234243'), BigInteger.new('354898905348354904533459583489345908980534908453089543089453543345')) --> 2398408903429083428902348234243

int BigInteger.compare(BigInteger left, BigInteger right)
Compares the value of BigInteger, if left is greater than right return 1, if less than right return -1 otherwise return 0, basically

if left > right then
	return 1
elseif left < right then
	return -1
end
return 0

BigInteger BigInteger.factorial(BigInteger value)
Gets the factorial of the BigInteger value

BigInteger BigInteger.gcd(BigInteger value)
Gets the greatest common divisor of the BigItneger value

double BigInteger.todouble(BigInteger value)
Converts the BigItneger value to Luau number (double)

string BigInteger.tostring(BigInteger value, table options)
Basic number formatting, you never know if you might need it :slight_smile:
Options
useGrouping
Determine whether to separate number or not.

decimalSymbol
The decimal symbol for scientfic and engineering notation, the default is ‘.’

groupSymbol
The group symbol for standard notation, the default is ’ ’ (U+2009)

minimumGroupingDigits
Suppress grouping below certain digits, the only valid value are from 1 to 4

minimumIntegerDigits
The minimum integer digits

notation
The formatting displayed for BigInteger.

  • "standard" for plain decimal formatting
  • "scientific" for scientific notation
  • "engineering" for engineering notation

numberingSystem
The numbering system to use, the default is ‘latn’

string BigInteger.tolocalestring(BigInteger value, string locale, table options)
A built-in locale based number formatting (don’t ask why I added this).
Options
useGrouping
Determine whether to separate number or not. This only acccept booleans, so true is "auto" (expect for compact and abbreviated notation which is "min2") and false is "never".

notation
The formatting displayed for BigInteger.

  • "standard" for plain decimal formatting
  • "scientific" for scientific notation
  • "engineering" for engineering notation
  • "compact" for compact notation, can only go up to trillion
  • "abbreviated" for abbreviated value maximum of 3 significant digits, can only go up to trillion

minimumIntegerDigits
The minimum of integer digits to use. Only works for standard notation.

numberingSystem
The numbering system to use, the default is locale-dependant

string tostring(BigInteger)
Converts BigInteger to string.

boolean BigInteger.isbiginteger(value)
Check if the value is BigItneger or not, only available on the standalone version.

Operators

BigInteger + BigInteger
Adds the two BigInteger value

BigInteger - BigIntegers
Subtracts the two BigInteger value

BigInteger * BigInteger
Multiplies the BigInteger value

BigInteger / BigInteger
Divides the BigInteger value (floor division)

BigInteger % BigInteger
Get the remainder of these two BigInteger value (note that it’s NOT modulus, -4 % 3 returns -1 for BigInteger but 2 for Luau numbers)

BigInteger ^ BigIntger
Power of the biginteger value

BigInteger > BigInteger
BigInteger < BigInteger
BigInteger >= BigInteger
BigInteger <= BigInteger
BigInteger == BigInteger
BigInteger ~= BigInteger
Compare BigInteger value depending on the operator, > is greater than, < is less than, >= is greater than or equals to, <= is less than or equals to, == is equals to and ~= is not equals to.

Where do I get it?

You can get it from GitHub or from here:
2.0.1:

2.0.0:

1.0.0:

  • BigInteger.lua (18·7 KiB) (No ToLocaleString)
  • 21 May 2020, 20.13.56 (CET)
73 Likes

Wow, this is actually incredibly useful!

Could you explain a bit about how it works for my curiosity?

5 Likes

Sorry for late reply.
It’s bascially storing in tables of base 2 { 0, 1, 1, 1, 0, 0 } in little-endian order, then convert it to tables of base 9.007.199.254.740.992, when doing arthmetric (addition, subtraction, mulitplication, division), it gets converted back to base 2 as 9.007.199.254.740.992 - 1 is the maximum safe integer a dobule can store (Lua 5.1 number uses double) and adding 1 to 9.007.199.254.740.992 would return 9.007.199.254.740.992 instead of 9.007.199.254.740.993. It’s bascially binary, and based on C#'s BigInteger. Nothing really special about this module, it’s actually quite easy to implement for me as I made this so many times before but all of the others I’ve made are full of bugs.

4 Likes

Update 2.0.0-alpha: I’ve reworked this, with many features substancially modified, however despite many bugs fixees, you might still encounter bugs and issues. If you are that interested in precise numbers there are far superior implementions out there, GNU GMP is one of them. This module (all versions) is not reliable to use for this by any means.

Updates and Features:

  • BigInteger.new(-1) < BigInteger(-2) now returns false as it should be, this is an obvious bug fixed that I didn’t notice for some reason.
  • BigInteger (not BigDecimal) now rawequals to each other, so you can index it without a doubt. But don’t be 100 % certain it will as this is the alpha version.
  • Added BigDecimal, so it supports decimals too.
  • Now it edits the bits directly when doing arithmetic instead of converting it to base 2 for nothing. Also includes signs so now it’s not stored in base 9 thousand long-scale billion (9.007.199.254.740.992) but base 18 thousand long-scale billion (18.014.398.509.481.984)
  • You can now index BigIntegers and do BigInteger:function(), most functions names for BigInteger are now in all lowercase. BigDecimal function names is still in pascal case.
  • Includes number abbreviation abbreviations from @berezaa’s MoneyLib, the function is BigInteger:shorten(). As for BigDecimal BigDecimal:ToLocaleString('en', { notation = "compact" }) is the closest thing you can achieve this.
  • It supports the Intl module, you can simplfy use BigInteger:tolocalestring() and BigInteger:torulebasedlocalestring() for rule based number formatting.
  • This version doesn’t have a scientific notation converter, if you want to convert exponents (e.g. 1E100), there’s always ToLocaleString, BigInteger:tolocalestring('en', { notation = "scientific" }). For BigDecimals there’s BigDecimal:ToExpString()

Notes:

  • BigDecimal Round/Quantize (bascially the same thing for now) function rounds 2.5 down to 2 but rounds 1.5 up to 2, this is normal and an expected behaviour, however you’re free to bypass this by using BigDecimal:Round(0, BigDecimal.MidpointRounding.AwayFromZero) or run BigDecimal.SetContext{ MdpointRounding = BigDecimal.MidpointRounding.AwayFromZero } beforehand.
  • If an invalid value is inputted BigDecimal constructor, it doesn’t return nil, but returns BigDecimal.NaN, if you’re not happy with this behaviour, you can run BigDecimal.SetContext{ InvalidValueReturnsNaN = false } beforehand.

However this module is still in alpha, many parts will be documented eventually.
You can get it from here, I bundled it with Intl (with many parts removed to save space):

4 Likes

This lua file is really useful!

This is a nice library, thanks for making it public. One problem I see with it is that it is pretty slow. I can get 14000 ADDITIONS per second with it. With “BigNum” I can get 352000 per second doing the same thing. Multiplication is also very slow, getting down into the 100s per second. Is there some solution to speed this up that I am overlooking?

2 Likes

I haven’t optimised the module and it’s directly dealing with base 2 (despite it’s stored in base 2 ** 54) while BigNum, uses base 2 ** 24, I use base 2 because division is more accurate via binary division and that’s the problem with BigNum, divsion of large numbers aren’t accurate. I gernerally am a person that values accuracy over speed. International before 2.1 was very slow but you get accurate and correct results.
If it were implemented in C-side, it would’e been definetly 1.000x faster, unforunately I cannot implement it C–side :frowning: , this would’ve been written in C if I can. Look at JavaScript BigInt polyfills before BigInt became primitive.
Version 2 is upcoming, while I don’t think it’ll be any faster, I did learn a lot after version 1.

2 Likes

I agree that accuracy is important. Do you have a test case for BigNum division being wrong? In regards to speed, if you use “number” in Lua then I think base 2^24 is for the best speed you are going to get.

I’ve implemented a uint128 and uint256 in C before, looking at what is possible in Roblox Lua we have bit32 available, which means you would have to do most of the calculations using 16bit values. Doing this you could represent any size integer you want and probably be quite “fast” doing it, in comparison to what we have to do right now a uint128 would be capable for nearly all Roblox games as it would allow up to undecillion values, uint256 would probably be overkill.

Here is a C++ implementation of a uint128 which is accurate, and if you look at its math functions would be fast even in Lua. I would recommend storing the values as 32bit “number” values and doing the math in 16bit steps. So a 128bit number would be stored in Lua as 256bits, double the size but compared to these other alternatives very space conscious. This code does things in 32bit steps (because it uses 64bit values which isn’t possible in Lua)

If you used the method BigNum does, where you can specify the number of 16bit “radix” you want and use for loops for the math you could do any size integer. Just for speed purposes it is best to avoid “for loops” and have a fixed size object.

1 Like

For a test case:

BigNum.new('680564693277057719623408366969033850880') / BigNum.new('85070591730234615847396907784232501249')
BigNum.new('680564693277057719623408366969033850880') % BigNum.new('85070591730234615847396907784232501249')

Returns 21 and 5.917.733.057.869.213.171.926.696.499.848.675.349 when the expected result is 7 and 85.070.551.165.415.408.691.630.012.479.406.342.137. That was my motivation as well as that I’m surpried to find out it was actually slower on BigNum with 100.000 microseconds on BigInteger and 5 mln microseconds on BigNum.

local f = Intl.NumberFormat.new(nil, { style = "unit", unit = "microsecond" });
local n = Numerics.BigInteger.new;
local a = os.clock();
for _ = 1, 100 do
	print(n'680564693277057719623408366969033850880':DivRem(n'85070591730234615847396907784232501249'));
end;
print(("BigInteger: The result was %s"):format(f:Format((os.clock() - a) * 1E6)));
a = os.clock();
for _ = 1, 100 do
	print(BigNum.new('680564693277057719623408366969033850880') / BigNum.new('85070591730234615847396907784232501249'));
end
print(("BigNum: The result was %s"):format(f:Format((os.clock() - a) * 1E6)));

7 85070551165415408691630012479406342137 (x100)
BigInteger: The result was 101.913,5 μs
21 (x100)
BigNum: The result was 5.990.684,8 μs

Testing this on version BigInteger 2.0.0 (which is private).

I changed from storing in base 2 ** 53 to direct directly storing in base 2 so I think that change makes it faster? But I’m planning to change from base 2 to 2 ** 52 (because 2 ** 53 - 1 is the maximum safe integer a double can store in Lua 5.1 and this is the highest value that it can do addition and subtraction with) then try to do binary division with it. I don’t think base 2 ** 24 (about 1,7 mln) is the best value for speed over 16 / 32 / 53, do you have an example where base 2 ** 24 is the fastest?
Is there a performance gain over storing in base 2 ** 32 (2 mrd), and what are the use case for bit32 over math.floor division (or the performance, I know nothing about this)?

What version of BigInteger are you using and how did you test it? It’s probably already obsolete as I’ve learnt so much along the way. (2.0.0 beta or 1.0.0?)

I not sure what other advantages this module has over BigNum tbh.

By the way, Lua ≥ 5.3 has 64-bit signed integer.

1 Like

Thanks for the test cases. I’m using the Big Integer 2.0 beta for the tests I ran. Roblox LUA is custom and uses roughly 5.1, though they backported bit32 from 5.2 to their version so that is available. I realize there is a 64bit integer support in latest Lua versions but unfortunately Roblox doesn’t support it and using Roblox IntValue is pointless.

I haven’t tested bit32 speed either, in theory it should be fast but until tests are done who knows. bitwise operations allow you to avoid divisions (which modulo is too) when doing multiplication and addition and other operations. Divisions tend to be slow and bignum is full of them, along with for loops.

As to why bignum uses 2^24 is mainly for the multiplication problem. ((2^24)-1) * ((2^24)-1) is a 2^48 number. So if you multiplied or added two large lua numbers together you are going to lose a large amount of integer precision. By keeping values to 2^24 you can ensure you will never overflow and lose that integer precision you want. As to why to keep values to 32bit range it means you will never overflow when you do your 16bit operations in the same way. If there was a bit64 support in Roblox we could do 32bit operations instead and nearly port that code I linked to 1:1

A library that clones the uint128 source I gave, but uses 16bit values instead of 32bit should be significantly faster than BigNum and what you are doing currently but any Roblox Lua overhead needs to be taken into consideration too. The Roblox team really need to support at least 256bit integers and 128bit doubles going forward in my opinion, not by default but give the option there for people who need it. Doing these math operations in Lua is too costly compared to C and will be running 5x slower at least than it would natively.

1 Like

Updated the module. For anyone that’s still on version 1 but cannot upgrade, here’s the documentation:

Version 1 documentation

BigInteger is a module I’ve made that therotically, can safely store and represent values up to 2⁹ ⁰⁰⁷ ¹⁹⁹ ²⁵⁴ ⁷⁴⁰ ⁹⁹² (2^2^53).
Why BigInteger you may ask?
Floating point after 2⁵³ cannot calculate number accurately and canont represent them safely.

print(('%.0f'):format(9007199254740992 + 1)) --> 9007199254740992
print(('%.0f'):format(10 ^ 100)) --> 10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104

This limitation doesn’t exist on BigInteger

print(BigInteger.new('9007199254740992') + BigInteger.new('1')) --> 9007199254740993
print(BigInteger.new('10') ^ BigInteger.new('100')) --> 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Lua 5.1 number uses a double-precision floating-point numbers specified in IEEE 754 and thus can only safely represent numbers from 2⁵³ - 1 to (2⁵³ - 1). BigInteger don’t have this problem.

API

BigInteger BigInteger.new(object value, int base)
Creates a new BigInteger, value is converted to string.

print(BigInteger.new(423)) --> BigInteger 423
print(BigInteger.new('8342')) --> BigInteger 8342

It also accepts spaces ( ), thin spaces ( ) (U+2009) and underscores (_) as long it’s in between the numbers

print(BigInteger.new('23 320 465 456')) --> BigInteger 23320465456
print(BigInteger.new('20 34 45 534')) --> BigInteger 203445534

…and exponents and standard form (base 10 only)

print(BigInteger.new('1E5')) --> BigInteger 100000
-- It accept decimals
print(BigInteger.new('1.5E3')) --> BigInteger 1500
-- It recognises commas as decimals, too.
print(BigInteger.new('1,5E3')) --> BigInteger 1500

…and numbers from base 2 to 36

print(BigInteger.new('9A', 12)) --> BigInteger 118

If the number isn’t valid, it’ll just return nil

print(BigInteger.new('Not a valid value')) --> nil
print(BigInteger.new(' 1')) --> nil
-- Doesn't accpet decimal outside of exponent
print(BigInteger.new('0.5')) --> nil
-- Too much decimal place for an expoenent
print(BigInteger.new('1.33333333333E1')) --> nil

BigInteger BigInteger.Zero
The zero value of the BigInteger

BigInteger BigInteger.One
The one value of the BigInteger

BigInteger BigInteger.MinusOne
Minus one value of the BigInteger

BigInteger BigInteger.Abs(BigInteger value)
Returns the absolute value of a BigInteger

print(BigInteger.Abs(BigInteger.new('-8972458972453897245978524789524897458297897452987'))); --> 8972458972453897245978524789524897458297897452987

BigInteger BigInteger.Pow(BigInteger left, BigInteger right)
Basically doing return left ^ right, just in case

BigInteger BigInteger.Shl(BigInteger x, int disp)
Returns the left shifted value of BigInteger, basically doing x << disp

BigInteger BigInteger.Shr(BigInteger x, int disp)
Returns the right shifed value of BigInteger, basically doing x >> disp

bool BigInteger.IsBigInteger(object value)
Check if the value is BigInteger or not

float BigInteger.Log(BigInteger value, float base)
Gets the logarithmn of the BigInteger value

float BigInteger.Log10(BigInteger value)
A sugar syntax for BigInteger.Log(value, 10)

float BigInteger.Log2(BigInteger value)
A sugar syntax for BigInteger.Log(value, 2)

BigInteger BigInteger.Max(params BigInteger[] value)
Gets the largest value of out of all the arguments. Accept floats too, but gets converted to BigInteger.

BigInteger BigInteger.Min(params BigInteger[] value)
Gets the smallest value of out of all the arguments. Accept floats too, but gets converted to BigInteger.

BigInteger BigInteger.DivRem(BigInteger divisor, BigInteger dividend)
Returns the divded value with the remainder, basically doing
return divisor / dividened, divisor % dividend

local q, r = BigInteger.DivRem(BigInteger.new('680564693277057719623408366969033850880'), BigInteger.new('85070591730234615847396907784232501249'))
print(q) --> 7
print(r) --> 85070551165415408691630012479406342137
local q1, r1 = BigInteger.DivRem(BigInteger.new('4267853996784526789431867986791348976413987634897613478961934876678914386974316897846791387694389671896743176'), BigInteger.new('36568725648674528765243876245386732543678852'));
print(q1) --> 116707758366723932202975984485653116311096030270662643956748580169
print(r1) --> 1704503777675545487297364693666244984857188

float BigInteger.TrueDiv(BigInteger dividened, BigInteger divisor)
Float division of the values, fixed on version 1.0.1

print(BigInteger.TrueDiv(BigInteger.new('357869879564378956478938973546542'), BigInteger.new('674464754765'))) --> 5.3059834044118e+20

int BigInteger.Sign(BigInteger values)
Gets the sign the of value

Value Sign
< 0 -1
= 0 0

0|1

string BigInteger.ToString(BigInteger value, table options)
Converts the BigInteger to string with options, this is similar to JS’s ToLocaleString option argument.
This is not locale aware

Option Default value Description
minimumSignificantDigits 1 The minimum number of digits
useGrouping false Group every 3 digits on the right by spaces ( )
minimumGroupingDigits 1 Don’t group below a certain value even though it can be grouped (Note: It’ll be overrided to 2, if the notation is “compact” and the value is 1)
notation “standard” Can be “scientific” (or “engineering”), “compact”, or “standardScientific”
decimalComma true Use commas instead of full stops (.) for decimal

float BigInteger.ToNumber(BigInteger value)
Converts the BigInteger value to float

Not implemented function

string BigInteger.ToLocaleString(BigInteger value, Locale locale, table options)
This is not implemented, this is here just in case
Rerurns localised version of the string

print(BigInteger.ToLocaleString(BigInteger.new('1234'), 'en_US')) --> 1,234
print(BigInteger.ToLocaleString(BigInteger.new('1234'), 'de_DE')) --> 1.234
print(BigInteger.ToLocaleString(BigInteger.new('1234'), 'es_ES')) --> 1234
print(BigInteger.ToLocaleString(BigInteger.new('1234'), 'es_ES')) --> 12.345
print(BigInteger.ToLocaleString(BigInteger.new('1234'), 'fr_FR')) --> 1 234

print(BigInteger.ToLocaleString(BigInteger.new('12345'), 'en_US', { notation = "compact" })) --> 12K
print(BigInteger.ToLocaleString(BigInteger.new('12345'), 'ja_JP', { notation = "compact" })) --> 1.2万
print(BigInteger.ToLocaleString(BigInteger.new('1234567'), 'de_DE', { notation = "compact" })) --> 1,2 Mio.

Operators

BigInteger + BigInteger
Adds the value, floats get converted to BigInteger automatically

BigInteger - BigInteger
Subtract the value, floats get converted to BigInteger automatically

BigInteger * BigInteger
Multiplies the value, floats get converted to BigInteger automatically

BigInteger / BigInteger
Divides the BigInteger value, floats get converted to BigInteger automatically. This is floor division

BigInteger ^ BigInteger
Pow of the BigInteger value, floats get converted to BigInteger automatically

BigInteger % BigInteger
Remainder of the BigInteger value, floats get converted to BigInteger automatically
This is NOT modulus

print(BigInteger.new('-19') % BigInteger.new('12')) --> -7
print(-19 % 12) --> 5

Some Questions Answered

Does this support decimals?
No, but I’m planning to make a BigDecimal module, with theoretically, a precision up to 2⁹ ⁰⁰⁷ ¹⁹⁹ ²⁵⁴ ⁷⁴⁰ ⁹⁹².

How do I convert to to string?
Just use tostring(value), or BigInteger.ToString(value, options) for more options.
Unfortunatly this is not locale-aware, this version does not have ToLocaleString built in.

What base is this stored in?
Base 2⁵³ or 9,007199254740992 × 10¹⁵ or 9 007 199 254 740 992, it gets converted to base 2 when doing artithmetic.

Wait, what, it’s stored in base 2⁵³?
Yes, it is stored in base 2⁵³, that’s not a typo

Where do I get it?

You can get it from GitHub or from here:
1.0.0:

  • BigInteger.lua (18·7 KiB) (No ToLocaleString)
  • 21 May 2020, 20.13.56 (CET)

So I’m working with RSA encryption, and I’m using this equation for testing: 1394^2011 mod(3127) and that is suppose to return 89, however when I write it in lua using this for the big number it has to go through:

   local m = (BigInteger.new(1394) ^ BigInteger.new(2011)) % BigInteger.new(3127)

it returns 1831, what am I doing wrong?

There was a bug where divsion and remainder returns the wrong value (I spotted this upon developing version 2.1).
This bug was patched in 2.1 which I’m still working on (but haven’t really got a time to do so) :frowning:

I’ve fixed this bug without the 2.1 changes for anyone using any part of the divrem function of BigInteger (/, % and the :divrem method), along with the bug where 0+BigInteger.new(-1) (or something like that?) errors:
Version 2.0.2: BigInteger.rbxm (23.0 KiB)

1 Like

So the version you uploaded is the fixed version?

Edit*: Tested it and it works, thanks soooo much, currently trying to do RSA encryption but that latest version has that bug.

Can BigInteger values be stored in a datastore? Or do I need to serialize them or something?

No you’ll have to serialise them, I didn’t create this for DataStores in mind.


NOTE: I no longer maintain the version of the module, I might re-implement the module at some point but I don’t have feel like re-implementing it right now.

1 Like

Wow! Great module! But how can I use it for leaderstats?

It wasn’t intended for leaderstats nor to bypass leaderstats’ IntValue that uses signed 64-bit integers, but to fix the precision loss Luau numbers has (because all numbers in Luau are IEEE 754 doubles) with all values only being integers (whether signed or unsigned), similar motivation to ES2020’s bigint; I will not expect you to use Values Instance in Roblox for this module but there’s nothing stopping you doing so.

If you don’t mind precision of only 15 digits and don’t need to go over 21024 (10308) then IEEE 754 doubles (Luau number representation) and NumberValue should suffice otherwise you could use StringValues and cast them to strings using tostring.

2 Likes

Wow, thank you so much!!
This saves me a lot of thinking of a solution to big number!! XD

found a minor grammar error here, thought I would help out