SkillAgentSearch skills...

Fraction.js

The RAW rational numbers library written in JavaScript

Install / Use

/learn @rawify/Fraction.js
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Fraction.js - ℚ in JavaScript

NPM Package MIT license

Do you find the limitations of floating-point arithmetic frustrating, especially when rational and irrational numbers like π or √2 are stored within the same finite precision? This can lead to avoidable inaccuracies such as:

1 / 98 * 98 // Results in 0.9999999999999999

For applications requiring higher precision or where working with fractions is preferable, consider incorporating Fraction.js into your project.

The library effectively addresses precision issues, as demonstrated below:

Fraction(1).div(98).mul(98) // Returns 1

Fraction.js uses a BigInt representation for both the numerator and denominator, ensuring minimal performance overhead while maximizing accuracy. Its design is optimized for precision, making it an ideal choice as a foundational library for other math tools, such as Polynomial.js and Math.js.

Convert Decimal to Fraction

One of the core features of Fraction.js is its ability to seamlessly convert decimal numbers into fractions.

let x = new Fraction(1.88);
let res = x.toFraction(true); // Returns "1 22/25" as a string

This is particularly useful when you need precise fraction representations instead of dealing with the limitations of floating-point arithmetic. What if you allow some error tolerance?

let x = new Fraction(0.33333);
let res = x.simplify(0.001) // Error < 0.001
       .toFraction(); // Returns "1/3" as a string

Precision

As native BigInt support in JavaScript becomes more common, libraries like Fraction.js use it to handle calculations with higher precision. This improves the speed and accuracy of math operations with large numbers, providing a better solution for tasks that need more precision than floating-point numbers can offer.

Examples / Motivation

A simple example of using Fraction.js might look like this:

var f = new Fraction("9.4'31'"); // 9.4313131313131...
f.mul([-4, 3]).mod("4.'8'"); // 4.88888888888888...

The result can then be displayed as:

console.log(f.toFraction()); // -4154 / 1485

Additionally, you can access the internal attributes of the fraction, such as the sign (s), numerator (n), and denominator (d). Keep in mind that these values are stored as BigInt:

Number(f.s) * Number(f.n) / Number(f.d) = -1 * 4154 / 1485 = -2.797306...

If you attempted to calculate this manually using floating-point arithmetic, you'd get something like:

(9.4313131 * (-4 / 3)) % 4.888888 = -2.797308133...

While the result is reasonably close, it’s not as accurate as the fraction-based approach that Fraction.js provides, especially when dealing with repeating decimals or complex operations. This highlights the value of precision that the library brings.

Laplace Probability

Here's a straightforward example of using Fraction.js to calculate probabilities. Let's determine the probability of rolling a specific outcome on a fair die:

  • P({3}): The probability of rolling a 3.
  • P({1, 4}): The probability of rolling either 1 or 4.
  • P({2, 4, 6}): The probability of rolling 2, 4, or 6.

P({3}):

var p = new Fraction([3].length, 6).toString(); // "0.1(6)"

P({1, 4}):

var p = new Fraction([1, 4].length, 6).toString(); // "0.(3)"

P({2, 4, 6}):

var p = new Fraction([2, 4, 6].length, 6).toString(); // "0.5"

Convert degrees/minutes/seconds to precise rational representation:

57+45/60+17/3600

var deg = 57; // 57°
var min = 45; // 45 Minutes
var sec = 17; // 17 Seconds

new Fraction(deg).add(min, 60).add(sec, 3600).toString() // -> 57.7547(2)

Rational approximation of irrational numbers

To approximate a number like sqrt(5) - 2 with a numerator and denominator, you can reformat the equation as follows: pow(n / d + 2, 2) = 5.

Then the following algorithm will generate the rational number besides the binary representation.

var x = "/", s = "";

var a = new Fraction(0),
    b = new Fraction(1);
for (var n = 0; n <= 10; n++) {

  var c = a.add(b).div(2);

  console.log(n + "\t" + a + "\t" + b + "\t" + c + "\t" + x);

  if (c.add(2).pow(2).valueOf() < 5) {
    a = c;
    x = "1";
  } else {
    b = c;
    x = "0";
  }
  s+= x;
}
console.log(s)

The result is

n   a[n]        b[n]        c[n]            x[n]
0   0/1         1/1         1/2             /
1   0/1         1/2         1/4             0
2   0/1         1/4         1/8             0
3   1/8         1/4         3/16            1
4   3/16        1/4         7/32            1
5   7/32        1/4         15/64           1
6   15/64       1/4         31/128          1
7   15/64       31/128      61/256          0
8   15/64       61/256      121/512         0
9   15/64       121/512     241/1024        0
10  241/1024    121/512     483/2048        1

Thus the approximation after 11 iterations of the bisection method is 483 / 2048 and the binary representation is 0.00111100011 (see WolframAlpha)

I published another example on how to approximate PI with fraction.js on my blog (Still not the best idea to approximate irrational numbers, but it illustrates the capabilities of Fraction.js perfectly).

Get the exact fractional part of a number

var f = new Fraction("-6.(3416)");
console.log(f.mod(1).abs().toFraction()); // = 3416/9999

Mathematical correct modulo

The behaviour on negative congruences is different to most modulo implementations in computer science. Even the mod() function of Fraction.js behaves in the typical way. To solve the problem of having the mathematical correct modulo with Fraction.js you could come up with this:

var a = -1;
var b = 10.99;

console.log(new Fraction(a)
  .mod(b)); // Not correct, usual Modulo

console.log(new Fraction(a)
  .mod(b).add(b).mod(b)); // Correct! Mathematical Modulo

fmod() imprecision circumvented

It turns out that Fraction.js outperforms almost any fmod() implementation, including JavaScript itself, php.js, C++, Python, Java and even Wolframalpha due to the fact that numbers like 0.05, 0.1, ... are infinite decimal in base 2.

The equation fmod(4.55, 0.05) gives 0.04999999999999957, wolframalpha says 1/20. The correct answer should be zero, as 0.05 divides 4.55 without any remainder.

Parser

Any function (see below) as well as the constructor of the Fraction class parses its input and reduce it to the smallest term.

You can pass either Arrays, Objects, Integers, Doubles or Strings.

Arrays / Objects

new Fraction(numerator, denominator);
new Fraction([numerator, denominator]);
new Fraction({n: numerator, d: denominator});

Integers

new Fraction(123);

Doubles

new Fraction(55.4);

Note: If you pass a double as it is, Fraction.js will perform a number analysis based on Farey Sequences. If you concern performance, cache Fraction.js objects and pass arrays/objects.

The method is really precise, but too large exact numbers, like 1234567.9991829 will result in a wrong approximation. If you want to keep the number as it is, convert it to a string, as the string parser will not perform any further observations. If you have problems with the approximation, in the file examples/approx.js is a different approximation algorithm, which might work better in some more specific use-cases.

Strings

new Fraction("123.45");
new Fraction("123/45"); // A rational number represented as two decimals, separated by a slash
new Fraction("123:45"); // A rational number represented as two decimals, separated by a colon
new Fraction("4 123/45"); // A rational number represented as a whole number and a fraction
new Fraction("123.'456'"); // Note the quotes, see below!
new Fraction("123.(456)"); // Note the brackets, see below!
new Fraction("123.45'6'"); // Note the quotes, see below!
new Fraction("123.45(6)"); // Note the brackets, see below!

Two arguments

new Fraction(3, 2); // 3/2 = 1.5

Repeating decimal places

Fraction.js can easily handle repeating decimal places. For example 1/3 is 0.3333.... There is only one repeating digit. As you can see in the examples above, you can pass a number like 1/3 as "0.'3'" or "0.(3)", which are synonym. There are no tests to parse something like 0.166666666 to 1/6! If you really want to handle this number, wrap around brackets on your own with the function below for example: 0.1(66666666)

Assume you want to divide 123.32 / 33.6(567). WolframAlpha states that you'll get a period of 1776 digits. Fraction.js comes to the same result. Give it a try:

var f = new Fraction("123.32");
console.log("Bam: " + f.div("33.6(567)"));

To automatically make a number like "0.123123123" to something more Fraction.js friendly like "0.(123)", I hacked this little brute force algorithm in a 10 minutes. Improvements are welcome...

function formatDecimal(str) {

  var comma, pre, offset, pad, times, repeat;

  if (-1 === (comma = str.indexOf(".")))
    return str;

  pre = str.substr(0, comma + 1);
  str = str.substr(comma + 1);

  for (var i = 0; i < str.length; i++) {

    offset = str.substr(0, i);

    for (var j = 0; j < 5; j++) {

      pad = str.s

Related Skills

View on GitHub
GitHub Stars684
CategoryDevelopment
Updated1d ago
Forks76

Languages

JavaScript

Security Score

100/100

Audited on Apr 2, 2026

No findings