GMP-WASM
Arbitrary-precision Integer, Rational and Float types based on the GMP and MPFR libraries.
Features
- Supports all modern browsers, web workers, Node.js and Deno
- Includes an easy-to-use, high-level wrapper, but low-level functions are also exposed
- Has a lot more features, and in some cases, it's faster than the built-in BigInt type
- The WASM binary is bundled as a compressed base64 string (no problems with linking)
- Works even without Webpack or other bundlers
- Includes TypeScript type definitions, check API here.
- Zero dependencies
- Full minified and gzipped bundle has a size of
- It also packages a mini bundle without Float/MPFR operations
- 100% open source & transparent build process
Installation
npm i gmp-wasm
It can also be used directly from HTML (via jsDelivr):
<script src="https://cdn.jsdelivr.net/npm/gmp-wasm"></script>
<script src="https://cdn.jsdelivr.net/npm/gmp-wasm/dist/index.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gmp-wasm/dist/mini.umd.min.js"></script>
Usage
gmp-wasm also provides a high-level wrapper over the GMP functions. There are three major components:
g.Integer()
- Wraps integers (MPZ)g.Rational()
- Wraps rational numbers (MPQ)g.Float()
- Wraps floating-point numbers (MPFR)
const gmp = require('gmp-wasm');
gmp.init().then(({ calculate }) => {
const result = calculate((g) => {
const six = g.Float(1).add(5);
return g.Pi().div(six).sin();
});
console.log(result);
});
It is also possible to delay deallocation through the getContext()
API:
const gmp = require('gmp-wasm');
gmp.init().then(({ getContext }) => {
const ctx = getContext();
let x = ctx.Integer(1);
for (let i = 2; i < 16; i++) {
x = x.add(i);
}
console.log(x.toString());
setTimeout(() => ctx.destroy(), 50);
});
The precision and the rounding modes can be set by passing a parameter to the context or to the Float constructor.
const roundingMode = gmp.FloatRoundingMode.ROUND_DOWN;
const options = { precisionBits: 10, roundingMode };
const result = calculate(g => g.Float(1).div(3), options);
const result2 = calculate(g => g.Float(1, options).div(3));
const ctx = getContext(options);
const result3 = ctx.Float(1).div(3).toString();
Predefined constants
- Pi
- EulerConstant
- EulerNumber
- Log2
- Catalan
Advanced usage
High-level wrapper can be combined with low-level functions:
const sum = calculate((g) => {
const a = g.Float(1);
const b = g.Float(2);
const c = g.Float(0);
binding.mpfr_add(c.mpfr_t, a.mpfr_t, b.mpfr_t, 0);
return c;
});
If you want more control and performance you can use the original GMP / MPFR functions even without high-level wrappers.
const gmp = require('gmp-wasm');
gmp.init().then(({ binding }) => {
const num1Ptr = binding.mpz_t();
binding.mpz_init_set_si(num1Ptr, 30);
const num2Ptr = binding.mpz_t();
const strPtr = binding.malloc_cstr('40');
binding.mpz_init_set_str(num2Ptr, strPtr, 10);
binding.mpz_add(num1Ptr, num1Ptr, num2Ptr);
console.log(binding.mpz_get_si(num1Ptr));
binding.free(strPtr);
binding.mpz_clears(num1Ptr, num2Ptr);
binding.mpz_t_frees(num1Ptr, num2Ptr);
});
Sometimes, it's easier and faster to deallocate everything by reinitializing the WASM bindings:
await binding.reset();
Performance
In some cases, this library can provide better performance than the built-in BigInt type.
For example, calculating 8000 digits of Pi using the following formula provides better results:
PI = 3
+ 3 * (1/2) * (1/3) * (1/4)
+ 3 * ((1 * 3)/(2 * 4)) * (1/5) * (1 / (4^2))
+ 3 * ((1 * 3 * 5) / (2 * 4 * 6)) * (1/7) * (1 / (4^3))
+ ...
Test | Avg. time | Speedup |
---|
With JS built-in BigInt type | 130.20 ms | 1x |
gmp-wasm Integer() high-level wrapper | 87.90 ms | 1.48x |
Same as previous with delayed memory deallocation | 78.88 ms | 1.65x |
gmp-wasm MPZ low-level functions | 53.93 ms | 2.41x |
decimal.js 10.3.1 with integer division | 426.99 ms | 0.30x |
big-integer 1.6.51 | 129.98 ms | 1x |
---------------------------- | -------- | -------- |
gmp-wasm Float() high-level wrapper | 198.31 ms | 0.66x |
Same as previous with delayed memory deallocation | 191.94 ms | 0.68x |
gmp-wasm MPFR low-level functions | 135.49 ms | 0.96x |
decimal.js 10.3.1 with float division | 764.15 ms | 0.17x |
---------------------------- | -------- | -------- |
gmp-wasm Float(1).atan().mul(4) | 0.65 ms | 200.31x |
gmp-wasm Float('0.5').asin().mul(6) | 17.21 ms | 7.57x |
* These measurements were made with Node.js v16.13
on an Intel Kaby Lake desktop CPU. Source code is here.