README.md (15812B)
1 # Fraction.js - ℚ in JavaScript 2 3 [](https://npmjs.org/package/fraction.js) 4 5 [](https://travis-ci.org/infusion/Fraction.js) 6 [](http://opensource.org/licenses/MIT) 7 8 9 Tired of inprecise numbers represented by doubles, which have to store rational and irrational numbers like PI or sqrt(2) the same way? Obviously the following problem is preventable: 10 11 ```javascript 12 1 / 98 * 98 // = 0.9999999999999999 13 ``` 14 15 If you need more precision or just want a fraction as a result, have a look at *Fraction.js*: 16 17 ```javascript 18 var Fraction = require('fraction.js'); 19 20 Fraction(1).div(98).mul(98) // = 1 21 ``` 22 23 Internally, numbers are represented as *numerator / denominator*, which adds just a little overhead. However, the library is written with performance in mind and outperforms any other implementation, as you can see [here](http://jsperf.com/convert-a-rational-number-to-a-babylonian-fractions/28). This basic data-type makes it the perfect basis for [Polynomial.js](https://github.com/infusion/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs). 24 25 Convert decimal to fraction 26 === 27 The simplest job for fraction.js is to get a fraction out of a decimal: 28 ```javascript 29 var x = new Fraction(1.88); 30 var res = x.toFraction(true); // String "1 22/25" 31 ``` 32 33 Examples / Motivation 34 === 35 A simple example might be 36 37 ```javascript 38 var f = new Fraction("9.4'31'"); // 9.4313131313131... 39 f.mul([-4, 3]).mod("4.'8'"); // 4.88888888888888... 40 ``` 41 The result is 42 43 ```javascript 44 console.log(f.toFraction()); // -4154 / 1485 45 ``` 46 You could of course also access the sign (s), numerator (n) and denominator (d) on your own: 47 ```javascript 48 f.s * f.n / f.d = -1 * 4154 / 1485 = -2.797306... 49 ``` 50 51 If you would try to calculate it yourself, you would come up with something like: 52 53 ```javascript 54 (9.4313131 * (-4 / 3)) % 4.888888 = -2.797308133... 55 ``` 56 57 Quite okay, but yea - not as accurate as it could be. 58 59 60 Laplace Probability 61 === 62 Simple example. What's the probability of throwing a 3, and 1 or 4, and 2 or 4 or 6 with a fair dice? 63 64 P({3}): 65 ```javascript 66 var p = new Fraction([3].length, 6).toString(); // 0.1(6) 67 ``` 68 69 P({1, 4}): 70 ```javascript 71 var p = new Fraction([1, 4].length, 6).toString(); // 0.(3) 72 ``` 73 74 P({2, 4, 6}): 75 ```javascript 76 var p = new Fraction([2, 4, 6].length, 6).toString(); // 0.5 77 ``` 78 79 Convert degrees/minutes/seconds to precise rational representation: 80 === 81 82 57+45/60+17/3600 83 ```javascript 84 var deg = 57; // 57° 85 var min = 45; // 45 Minutes 86 var sec = 17; // 17 Seconds 87 88 new Fraction(deg).add(min, 60).add(sec, 3600).toString() // -> 57.7547(2) 89 ``` 90 91 Rounding a fraction to the closest tape measure value 92 === 93 94 A tape measure is usually divided in parts of `1/16`. Rounding a given fraction to the closest value on a tape measure can be determined by 95 96 ```javascript 97 function closestTapeMeasure(frac) { 98 99 /* 100 k/16 ≤ a/b < (k+1)/16 101 ⇔ k ≤ 16*a/b < (k+1) 102 ⇔ k = floor(16*a/b) 103 */ 104 return new Fraction(Math.round(16 * Fraction(frac).valueOf()), 16); 105 } 106 // closestTapeMeasure("1/3") // 5/16 107 ``` 108 109 Rational approximation of irrational numbers 110 === 111 112 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*. 113 114 Then the following algorithm will generate the rational number besides the binary representation. 115 116 ```javascript 117 var x = "/", s = ""; 118 119 var a = new Fraction(0), 120 b = new Fraction(1); 121 for (var n = 0; n <= 10; n++) { 122 123 var c = a.add(b).div(2); 124 125 console.log(n + "\t" + a + "\t" + b + "\t" + c + "\t" + x); 126 127 if (c.add(2).pow(2) < 5) { 128 a = c; 129 x = "1"; 130 } else { 131 b = c; 132 x = "0"; 133 } 134 s+= x; 135 } 136 console.log(s) 137 ``` 138 139 The result is 140 141 ``` 142 n a[n] b[n] c[n] x[n] 143 0 0/1 1/1 1/2 / 144 1 0/1 1/2 1/4 0 145 2 0/1 1/4 1/8 0 146 3 1/8 1/4 3/16 1 147 4 3/16 1/4 7/32 1 148 5 7/32 1/4 15/64 1 149 6 15/64 1/4 31/128 1 150 7 15/64 31/128 61/256 0 151 8 15/64 61/256 121/512 0 152 9 15/64 121/512 241/1024 0 153 10 241/1024 121/512 483/2048 1 154 ``` 155 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)) 156 157 158 I published another example on how to approximate PI with fraction.js on my [blog](http://www.xarg.org/2014/03/precise-calculations-in-javascript/) (Still not the best idea to approximate irrational numbers, but it illustrates the capabilities of Fraction.js perfectly). 159 160 161 Get the exact fractional part of a number 162 --- 163 ```javascript 164 var f = new Fraction("-6.(3416)"); 165 console.log("" + f.mod(1).abs()); // Will print 0.(3416) 166 ``` 167 168 Mathematical correct modulo 169 --- 170 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: 171 172 ```javascript 173 var a = -1; 174 var b = 10.99; 175 176 console.log(new Fraction(a) 177 .mod(b)); // Not correct, usual Modulo 178 179 console.log(new Fraction(a) 180 .mod(b).add(b).mod(b)); // Correct! Mathematical Modulo 181 ``` 182 183 fmod() impreciseness circumvented 184 --- 185 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. 186 187 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. 188 189 190 Parser 191 === 192 193 Any function (see below) as well as the constructor of the *Fraction* class parses its input and reduce it to the smallest term. 194 195 You can pass either Arrays, Objects, Integers, Doubles or Strings. 196 197 Arrays / Objects 198 --- 199 ```javascript 200 new Fraction(numerator, denominator); 201 new Fraction([numerator, denominator]); 202 new Fraction({n: numerator, d: denominator}); 203 ``` 204 205 Integers 206 --- 207 ```javascript 208 new Fraction(123); 209 ``` 210 211 Doubles 212 --- 213 ```javascript 214 new Fraction(55.4); 215 ``` 216 217 **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. 218 219 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. 220 221 222 Strings 223 --- 224 ```javascript 225 new Fraction("123.45"); 226 new Fraction("123/45"); // A rational number represented as two decimals, separated by a slash 227 new Fraction("123:45"); // A rational number represented as two decimals, separated by a colon 228 new Fraction("4 123/45"); // A rational number represented as a whole number and a fraction 229 new Fraction("123.'456'"); // Note the quotes, see below! 230 new Fraction("123.(456)"); // Note the brackets, see below! 231 new Fraction("123.45'6'"); // Note the quotes, see below! 232 new Fraction("123.45(6)"); // Note the brackets, see below! 233 ``` 234 235 Two arguments 236 --- 237 ```javascript 238 new Fraction(3, 2); // 3/2 = 1.5 239 ``` 240 241 Repeating decimal places 242 --- 243 *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) 244 245 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: 246 247 ```javascript 248 var f = new Fraction("123.32"); 249 console.log("Bam: " + f.div("33.6(567)")); 250 ``` 251 252 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... 253 254 ```javascript 255 function formatDecimal(str) { 256 257 var comma, pre, offset, pad, times, repeat; 258 259 if (-1 === (comma = str.indexOf("."))) 260 return str; 261 262 pre = str.substr(0, comma + 1); 263 str = str.substr(comma + 1); 264 265 for (var i = 0; i < str.length; i++) { 266 267 offset = str.substr(0, i); 268 269 for (var j = 0; j < 5; j++) { 270 271 pad = str.substr(i, j + 1); 272 273 times = Math.ceil((str.length - offset.length) / pad.length); 274 275 repeat = new Array(times + 1).join(pad); // Silly String.repeat hack 276 277 if (0 === (offset + repeat).indexOf(str)) { 278 return pre + offset + "(" + pad + ")"; 279 } 280 } 281 } 282 return null; 283 } 284 285 var f, x = formatDecimal("13.0123123123"); // = 13.0(123) 286 if (x !== null) { 287 f = new Fraction(x); 288 } 289 ``` 290 291 Attributes 292 === 293 294 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. 295 296 ```javascript 297 var f = new Fraction('-1/2'); 298 console.log(f.n); // Numerator: 1 299 console.log(f.d); // Denominator: 2 300 console.log(f.s); // Sign: -1 301 ``` 302 303 304 Functions 305 === 306 307 Fraction abs() 308 --- 309 Returns the actual number without any sign information 310 311 Fraction neg() 312 --- 313 Returns the actual number with flipped sign in order to get the additive inverse 314 315 Fraction add(n) 316 --- 317 Returns the sum of the actual number and the parameter n 318 319 Fraction sub(n) 320 --- 321 Returns the difference of the actual number and the parameter n 322 323 Fraction mul(n) 324 --- 325 Returns the product of the actual number and the parameter n 326 327 Fraction div(n) 328 --- 329 Returns the quotient of the actual number and the parameter n 330 331 Fraction pow(exp) 332 --- 333 Returns the power of the actual number, raised to an possible rational exponent. If the result becomes non-rational the function returns `null`. 334 335 Fraction mod(n) 336 --- 337 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 will. 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). 338 339 Fraction mod() 340 --- 341 Returns the modulus (rest of the division) of the actual object (numerator mod denominator) 342 343 Fraction gcd(n) 344 --- 345 Returns the fractional greatest common divisor 346 347 Fraction lcm(n) 348 --- 349 Returns the fractional least common multiple 350 351 Fraction ceil([places=0-16]) 352 --- 353 Returns the ceiling of a rational number with Math.ceil 354 355 Fraction floor([places=0-16]) 356 --- 357 Returns the floor of a rational number with Math.floor 358 359 Fraction round([places=0-16]) 360 --- 361 Returns the rational number rounded with Math.round 362 363 Fraction inverse() 364 --- 365 Returns the multiplicative inverse of the actual number (n / d becomes d / n) in order to get the reciprocal 366 367 Fraction simplify([eps=0.001]) 368 --- 369 Simplifies the rational number under a certain error threshold. Ex. `0.333` will be `1/3` with `eps=0.001` 370 371 boolean equals(n) 372 --- 373 Check if two numbers are equal 374 375 int compare(n) 376 --- 377 Compare two numbers. 378 ``` 379 result < 0: n is greater than actual number 380 result > 0: n is smaller than actual number 381 result = 0: n is equal to the actual number 382 ``` 383 384 boolean divisible(n) 385 --- 386 Check if two numbers are divisible (n divides this) 387 388 double valueOf() 389 --- 390 Returns a decimal representation of the fraction 391 392 String toString([decimalPlaces=15]) 393 --- 394 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. 395 396 **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! 397 398 String toLatex(excludeWhole=false) 399 --- 400 Generates an exact LaTeX representation of the actual object. You can see a [live demo](http://www.xarg.org/2014/03/precise-calculations-in-javascript/) on my blog. 401 402 The optional boolean parameter indicates if you want to exclude the whole part. "1 1/3" instead of "4/3" 403 404 String toFraction(excludeWhole=false) 405 --- 406 Gets a string representation of the fraction 407 408 The optional boolean parameter indicates if you want to exclude the whole part. "1 1/3" instead of "4/3" 409 410 Array toContinued() 411 --- 412 Gets an array of the fraction represented as a continued fraction. The first element always contains the whole part. 413 414 ```javascript 415 var f = new Fraction('88/33'); 416 var c = f.toContinued(); // [2, 1, 2] 417 ``` 418 419 Fraction clone() 420 --- 421 Creates a copy of the actual Fraction object 422 423 424 Exceptions 425 === 426 If a really hard error occurs (parsing error, division by zero), *fraction.js* throws exceptions! Please make sure you handle them correctly. 427 428 429 430 Installation 431 === 432 Installing fraction.js is as easy as cloning this repo or use one of the following commands: 433 434 ``` 435 bower install fraction.js 436 ``` 437 or 438 439 ``` 440 npm install fraction.js 441 ``` 442 443 Using Fraction.js with the browser 444 === 445 ```html 446 <script src="fraction.js"></script> 447 <script> 448 console.log(Fraction("123/456")); 449 </script> 450 ``` 451 452 Using Fraction.js with require.js 453 === 454 ```html 455 <script src="require.js"></script> 456 <script> 457 requirejs(['fraction.js'], 458 function(Fraction) { 459 console.log(Fraction("123/456")); 460 }); 461 </script> 462 ``` 463 464 Using Fraction.js with TypeScript 465 === 466 ```js 467 import Fraction from "fraction.js"; 468 console.log(Fraction("123/456")); 469 ``` 470 471 Coding Style 472 === 473 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. 474 475 476 Precision 477 === 478 Fraction.js tries to circumvent floating point errors, by having an internal representation of numerator and denominator. As it relies on JavaScript, there is also a limit. The biggest number representable is `Number.MAX_SAFE_INTEGER / 1` and the smallest is `-1 / Number.MAX_SAFE_INTEGER`, with `Number.MAX_SAFE_INTEGER=9007199254740991`. If this is not enough, there is `bigfraction.js` shipped experimentally, which relies on `BigInt` and should become the new Fraction.js eventually. 479 480 Testing 481 === 482 If you plan to enhance the library, make sure you add test cases and all the previous tests are passing. You can test the library with 483 484 ``` 485 npm test 486 ``` 487 488 489 Copyright and licensing 490 === 491 Copyright (c) 2014-2019, [Robert Eisele](https://www.xarg.org/) 492 Dual licensed under the MIT or GPL Version 2 licenses.