Gmp Wasm

Arbitrary-precision Integer, Rational and Float types based on the GMP and MPFR libraries

GMP-WASM

npm package codecov Build status JSDelivr downloads

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 Bundle size
  • It also packages a mini bundle without Float/MPFR operations Bundle size
  • 100% open source & transparent build process

Installation

npm i gmp-wasm

It can also be used directly from HTML (via jsDelivr):

<!-- loads the full, minified library into the global `gmp` variable -->
<script src="https://cdn.jsdelivr.net/npm/gmp-wasm"></script>

<!-- or loads the non-minified library -->
<script src="https://cdn.jsdelivr.net/npm/gmp-wasm/dist/index.umd.js"></script>

<!-- or loads the minified library without Float/MPFR functions -->
<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 }) => {
  // calculate() automatically deallocates all objects created within the callback function
  const result = calculate((g) => {
    const six = g.Float(1).add(5);
    return g.Pi().div(six).sin(); // sin(Pi/6) = 0.5
  });
  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);
// or
const result2 = calculate(g => g.Float(1, options).div(3));
// or
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);
  // c = a + b
  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 }) => {
  // Create first number and initialize it to 30
  const num1Ptr = binding.mpz_t();
  binding.mpz_init_set_si(num1Ptr, 30);
  // Create second number from string. The string needs to be copied into WASM memory
  const num2Ptr = binding.mpz_t();
  const strPtr = binding.malloc_cstr('40');
  binding.mpz_init_set_str(num2Ptr, strPtr, 10);
  // Calculate num1Ptr + num2Ptr, store the result in num1Ptr
  binding.mpz_add(num1Ptr, num1Ptr, num2Ptr);
  // Get result as integer
  console.log(binding.mpz_get_si(num1Ptr));
  // Deallocate memory
  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:

// Deallocate all memory objects created by gmp-wasm
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 129 ms 1x
gmp-wasm Integer() high-level wrapper 88 ms 1.47x
Same as previous with delayed memory deallocation 78 ms 1.65x
gmp-wasm MPZ low-level functions 53 ms 2.43x
decimal.js 10.3.1 with integer division 443 ms 0.29x
big-integer 1.6.51 129 ms 1x
---------------------------- -------- --------
gmp-wasm Float() high-level wrapper 175 ms 0.74x
Same as previous with delayed memory deallocation 169 ms 0.76x
gmp-wasm MPFR low-level functions 118 ms 1.09x
decimal.js 10.3.1 with float division 785 ms 0.16x
---------------------------- -------- --------
gmp-wasm Float(1).atan().mul(4) 0.6 ms 215x
gmp-wasm Float('0.5').asin().mul(6) 17 ms 7.59x

* These measurements were made with Node.js v16.14 on an Intel Kaby Lake desktop CPU. Source code is here.

Popular Bindings Projects
Popular Arbitrary Precision Projects
Popular Libraries Categories
Related Searches

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.
Javascript
Typescript
Bindings
Arbitrary Precision