Product
Introducing Ruby Support in Socket
Socket is launching Ruby support for all users. Enhance your Rails projects with AI-powered security scans for vulnerabilities and supply chain threats. Now in Beta!
fraction.js
Advanced tools
The fraction.js npm package is a library for handling fractional values in JavaScript. It allows for the creation, manipulation, and conversion of fractions, providing a way to perform arithmetic operations and comparisons with fractions.
Creating fractions
This feature allows for the creation of fraction instances from strings, numbers, or other fractions.
const Fraction = require('fraction.js');
let fraction = new Fraction('9/5');
Arithmetic operations
This feature enables the performance of arithmetic operations such as addition, subtraction, multiplication, and division between fractions.
let result = new Fraction(1, 2).add(new Fraction(1, 4));
Comparison operations
This feature allows for the comparison of fractions to check for equality, or to determine which is greater or less than the other.
let isEqual = new Fraction(1, 2).equals(new Fraction(2, 4));
Conversion to other formats
This feature provides methods to convert fractions to other formats, such as decimal values or strings.
let decimal = new Fraction(3, 4).valueOf();
Math.js is an extensive math library for JavaScript and Node.js, which supports fractions as well as a wide range of other mathematical functions and data types. It is more comprehensive than fraction.js but also larger in size.
Decimal.js is a library for arbitrary-precision decimal arithmetic. While it does not handle fractions directly, it can perform precise arithmetic operations which are useful when dealing with decimals that would otherwise be represented as fractions.
Big.js is a small, fast JavaScript library for arbitrary-precision arithmetic with big numbers. Like decimal.js, it does not handle fractions per se but is useful for high precision arithmetic.
Algebra.js is a library that includes fractions as part of its functionality to build and solve algebraic expressions. It provides a different approach by focusing on algebraic structures rather than just fractions.
Are you frustrated by the imprecision of floating-point numbers like doubles, which store both rational and irrational numbers such as π or √2 in the same limited way? This often leads to preventable issues like:
1 / 98 * 98 // = 0.9999999999999999
If you require greater precision or prefer to work with fractions instead of decimals, you can easily integrate Fraction.js into your project:
import Fraction from 'fraction.js';
// or
var Fraction = require('fraction.js');
Here’s an example of how it resolves the issue:
Fraction(1).div(98).mul(98) // = 1
Internally, Fraction.js represents numbers as numerator/denominator, adding only minimal overhead. The library is optimized for both performance and precision, making it an excellent foundation for other libs like Polynomial.js and Math.js.
One of the core functionalities of Fraction.js is its ability to convert decimals into fractions with ease:
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
With the growing adoption of native BigInt
support in JavaScript, libraries like Fraction.js have embraced it for handling arbitrary precision. This enables enhanced performance and accuracy in mathematical operations involving large integers, offering a more reliable solution for applications requiring precision beyond the typical constraints of floating-point Number
types.
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 components 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.
A simple example of using Fraction.js to calculate probabilities. Let’s find the probability of throwing specific outcomes with a fair die:
var p = new Fraction([3].length, 6).toString(); // "0.1(6)"
var p = new Fraction([1, 4].length, 6).toString(); // "0.(3)"
var p = new Fraction([2, 4, 6].length, 6).toString(); // "0.5"
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)
Now it's getting messy ;d 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).
var f = new Fraction("-6.(3416)");
console.log(f.mod(1).abs().toFraction()); // = 3416/9999
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
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.
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.
new Fraction(numerator, denominator);
new Fraction([numerator, denominator]);
new Fraction({n: numerator, d: denominator});
new Fraction(123);
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.
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!
new Fraction(3, 2); // 3/2 = 1.5
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.substr(i, j + 1);
times = Math.ceil((str.length - offset.length) / pad.length);
repeat = new Array(times + 1).join(pad); // Silly String.repeat hack
if (0 === (offset + repeat).indexOf(str)) {
return pre + offset + "(" + pad + ")";
}
}
}
return null;
}
var f, x = formatDecimal("13.0123123123"); // = 13.0(123)
if (x !== null) {
f = new Fraction(x);
}
The Fraction object allows direct access to the numerator, denominator and sign attributes. It is ensured that only the sign-attribute holds sign information so that a sign comparison is only necessary against this attribute.
var f = new Fraction('-1/2');
console.log(f.n); // Numerator: 1
console.log(f.d); // Denominator: 2
console.log(f.s); // Sign: -1
Returns the actual number without any sign information
Returns the actual number with flipped sign in order to get the additive inverse
Returns the sum of the actual number and the parameter n
Returns the difference of the actual number and the parameter n
Returns the product of the actual number and the parameter n
Returns the quotient of the actual number and the parameter n
Returns the power of the actual number, raised to an possible rational exponent. If the result becomes non-rational the function returns null
.
Returns the modulus (rest of the division) of the actual object and n (this % n). It's a much more precise fmod() if you like. Please note that mod() is just like the modulo operator of most programming languages. If you want a mathematical correct modulo, see here.
Returns the modulus (rest of the division) of the actual object (numerator mod denominator)
Returns the fractional greatest common divisor
Returns the fractional least common multiple
Returns the ceiling of a rational number with Math.ceil
Returns the floor of a rational number with Math.floor
Returns the rational number rounded with Math.round
Rounds a fraction to the closest multiple of another fraction.
Returns the multiplicative inverse of the actual number (n / d becomes d / n) in order to get the reciprocal
Simplifies the rational number under a certain error threshold. Ex. 0.333
will be 1/3
with eps=0.001
Check if two numbers are equal
Compare two numbers.
result < 0: n is greater than actual number
result > 0: n is smaller than actual number
result = 0: n is equal to the actual number
Check if two numbers are divisible (n divides this)
Returns a decimal representation of the fraction
Generates an exact string representation of the actual object. For repeated decimal places all digits are collected within brackets, like 1/3 = "0.(3)"
. For all other numbers, up to decimalPlaces
significant digits are collected - which includes trailing zeros if the number is getting truncated. However, 1/2 = "0.5"
without trailing zeros of course.
Note: As valueOf()
and toString()
are provided, toString()
is only called implicitly in a real string context. Using the plus-operator like "123" + new Fraction
will call valueOf(), because JavaScript tries to combine two primitives first and concatenates them later, as string will be the more dominant type. alert(new Fraction)
or String(new Fraction)
on the other hand will do what you expect. If you really want to have control, you should call toString()
or valueOf()
explicitly!
Generates an exact LaTeX representation of the actual object. You can see a live demo on my blog.
The optional boolean parameter indicates if you want to show the a mixed fraction. "1 1/3" instead of "4/3"
Gets a string representation of the fraction
The optional boolean parameter indicates if you want to showa mixed fraction. "1 1/3" instead of "4/3"
Gets an array of the fraction represented as a continued fraction. The first element always contains the whole part.
var f = new Fraction('88/33');
var c = f.toContinued(); // [2, 1, 2]
Creates a copy of the actual Fraction object
If a really hard error occurs (parsing error, division by zero), Fraction.js throws exceptions! Please make sure you handle them correctly.
Installing fraction.js is as easy as cloning this repo or use the following command:
npm install fraction.js
<script src="fraction.min.js"></script>
<script>
console.log(Fraction("123/456"));
</script>
import Fraction from "fraction.js";
console.log(Fraction("123/456"));
As every library I publish, Fraction.js is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
After cloning the Git repository run:
npm install
npm run build
Testing the source against the shipped test suite is as easy as
npm run test
Copyright (c) 2024, Robert Eisele Licensed under the MIT license.
FAQs
A rational numbers library
The npm package fraction.js receives a total of 14,194,145 weekly downloads. As such, fraction.js popularity was classified as popular.
We found that fraction.js demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket is launching Ruby support for all users. Enhance your Rails projects with AI-powered security scans for vulnerabilities and supply chain threats. Now in Beta!
Product
Ensure open-source compliance with Socket’s License Enforcement Beta. Set up your License Policy and secure your software!
Product
We're launching a new set of license analysis and compliance features for analyzing, managing, and complying with licenses across a range of supported languages and ecosystems.