Spaces:
Runtime error
Runtime error
| # Fraction.js - ℚ in JavaScript | |
| [](https://npmjs.org/package/fraction.js "View this project on npm") | |
| [](http://opensource.org/licenses/MIT) | |
| 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: | |
| ```javascript | |
| 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: | |
| ```javascript | |
| 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](https://github.com/rawify/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs). | |
| ## Convert Decimal to Fraction | |
| One of the core features of *Fraction.js* is its ability to seamlessly convert decimal numbers into fractions. | |
| ```javascript | |
| 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? | |
| ```javascript | |
| 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: | |
| ```javascript | |
| 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: | |
| ```javascript | |
| 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`: | |
| ```javascript | |
| 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: | |
| ```javascript | |
| (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}): | |
| ```javascript | |
| var p = new Fraction([3].length, 6).toString(); // "0.1(6)" | |
| ``` | |
| #### P({1, 4}): | |
| ```javascript | |
| var p = new Fraction([1, 4].length, 6).toString(); // "0.(3)" | |
| ``` | |
| #### P({2, 4, 6}): | |
| ```javascript | |
| 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 | |
| ```javascript | |
| 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. | |
| ```javascript | |
| 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](http://www.wolframalpha.com/input/?i=sqrt%285%29-2+binary)) | |
| I published another example on how to approximate PI with fraction.js on my [blog](https://raw.org/article/rational-numbers-in-javascript/) (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 | |
| ```javascript | |
| 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: | |
| ```javascript | |
| 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](http://phpjs.org/functions/fmod/), 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 | |
| ```javascript | |
| new Fraction(numerator, denominator); | |
| new Fraction([numerator, denominator]); | |
| new Fraction({n: numerator, d: denominator}); | |
| ``` | |
| ### Integers | |
| ```javascript | |
| new Fraction(123); | |
| ``` | |
| ### Doubles | |
| ```javascript | |
| 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 | |
| ```javascript | |
| 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 | |
| ```javascript | |
| 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](http://www.wolframalpha.com/input/?i=123.32+%2F+%2812453%2F370%29) states that you'll get a period of 1776 digits. *Fraction.js* comes to the same result. Give it a try: | |
| ```javascript | |
| 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... | |
| ```javascript | |
| 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); | |
| } | |
| ``` | |
| ## Attributes | |
| 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. | |
| ```javascript | |
| var f = new Fraction('-1/2'); | |
| console.log(f.n); // Numerator: 1 | |
| console.log(f.d); // Denominator: 2 | |
| console.log(f.s); // Sign: -1 | |
| ``` | |
| ## Functions | |
| ### Fraction abs() | |
| Returns the actual number without any sign information | |
| ### Fraction neg() | |
| Returns the actual number with flipped sign in order to get the additive inverse | |
| ### Fraction add(n) | |
| Returns the sum of the actual number and the parameter n | |
| ### Fraction sub(n) | |
| Returns the difference of the actual number and the parameter n | |
| ### Fraction mul(n) | |
| Returns the product of the actual number and the parameter n | |
| ### Fraction div(n) | |
| Returns the quotient of the actual number and the parameter n | |
| ### Fraction pow(exp) | |
| Returns the power of the actual number, raised to an possible rational exponent. If the result becomes non-rational the function returns `null`. | |
| ### Fraction log(base) | |
| Returns the logarithm of the actual number to a given rational base. If the result becomes non-rational the function returns `null`. | |
| ### Fraction mod(n) | |
| Returns the modulus (rest of the division) of the actual object and n (this % n). It's a much more precise [fmod()](#fmod-impreciseness-circumvented) 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](#mathematical-correct-modulo). | |
| ### Fraction mod() | |
| Returns the modulus (rest of the division) of the actual object (numerator mod denominator) | |
| ### Fraction gcd(n) | |
| Returns the fractional greatest common divisor | |
| ### Fraction lcm(n) | |
| Returns the fractional least common multiple | |
| ### Fraction ceil([places=0-16]) | |
| Returns the ceiling of a rational number with Math.ceil | |
| ### Fraction floor([places=0-16]) | |
| Returns the floor of a rational number with Math.floor | |
| ### Fraction round([places=0-16]) | |
| Returns the rational number rounded with Math.round | |
| ### Fraction roundTo(multiple) | |
| Rounds a fraction to the closest multiple of another fraction. | |
| ### Fraction inverse() | |
| Returns the multiplicative inverse of the actual number (n / d becomes d / n) in order to get the reciprocal | |
| ### Fraction simplify([eps=0.001]) | |
| Simplifies the rational number under a certain error threshold. Ex. `0.333` will be `1/3` with `eps=0.001` | |
| ### boolean equals(n) | |
| Check if two rational numbers are equal | |
| ### boolean lt(n) | |
| Check if this rational number is less than another | |
| ### boolean lte(n) | |
| Check if this rational number is less than or equal another | |
| ### boolean gt(n) | |
| Check if this rational number is greater than another | |
| ### boolean gte(n) | |
| Check if this rational number is greater than or equal another | |
| ### int compare(n) | |
| 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 | |
| ``` | |
| ### boolean divisible(n) | |
| Check if two numbers are divisible (n divides this) | |
| ### double valueOf() | |
| Returns a decimal representation of the fraction | |
| ### String toString([decimalPlaces=15]) | |
| Generates an exact string representation of the given object. For repeating decimal places, digits within repeating cycles are enclosed in parentheses, e.g., `1/3 = "0.(3)"`. For other numbers, the string will include up to the specified `decimalPlaces` significant digits, including any trailing zeros if truncation occurs. For example, `1/2` will be represented as `"0.5"`, without additional trailing zeros. | |
| **Note:** Since both `valueOf()` and `toString()` are provided, `toString()` will only be invoked implicitly when the object is used in a string context. For instance, when using the plus operator like `"123" + new Fraction`, `valueOf()` will be called first, as JavaScript attempts to combine primitives before concatenating them, with the string type taking precedence. However, `alert(new Fraction)` or `String(new Fraction)` will behave as expected. To ensure specific behavior, explicitly call either `toString()` or `valueOf()`. | |
| ### String toLatex(showMixed=false) | |
| Generates an exact LaTeX representation of the actual object. You can see a [live demo](https://raw.org/article/rational-numbers-in-javascript/) on my blog. | |
| The optional boolean parameter indicates if you want to show the a mixed fraction. "1 1/3" instead of "4/3" | |
| ### String toFraction(showMixed=false) | |
| 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" | |
| ### Array toContinued() | |
| Gets an array of the fraction represented as a continued fraction. The first element always contains the whole part. | |
| ```javascript | |
| var f = new Fraction('88/33'); | |
| var c = f.toContinued(); // [2, 1, 2] | |
| ``` | |
| ### Fraction clone() | |
| Creates a copy of the actual Fraction object | |
| ## Exceptions | |
| If a really hard error occurs (parsing error, division by zero), *Fraction.js* throws exceptions! Please make sure you handle them correctly. | |
| ## Installation | |
| You can install `Fraction.js` via npm: | |
| ```bash | |
| npm install fraction.js | |
| ``` | |
| Or with yarn: | |
| ```bash | |
| yarn add fraction.js | |
| ``` | |
| Alternatively, download or clone the repository: | |
| ```bash | |
| git clone https://github.com/rawify/Fraction.js | |
| ``` | |
| ## Usage | |
| Include the `fraction.min.js` file in your project: | |
| ```html | |
| <script src="path/to/fraction.min.js"></script> | |
| <script> | |
| var x = new Fraction("13/4"); | |
| </script> | |
| ``` | |
| Or in a Node.js project: | |
| ```javascript | |
| const Fraction = require('fraction.js'); | |
| ``` | |
| or | |
| ```javascript | |
| import Fraction from 'fraction.js'; | |
| ``` | |
| ## Coding Style | |
| 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. | |
| ## Building the library | |
| After cloning the Git repository run: | |
| ```bash | |
| npm install | |
| npm run build | |
| ``` | |
| ## Run a test | |
| Testing the source against the shipped test suite is as easy as | |
| ```bash | |
| npm run test | |
| ``` | |
| ## Copyright and Licensing | |
| Copyright (c) 2025, [Robert Eisele](https://raw.org/) | |
| Licensed under the MIT license. | |