Skip to content

Commit

Permalink
Update toFraction and toMixedNumber functions
Browse files Browse the repository at this point in the history
  • Loading branch information
plegner committed Sep 22, 2023
1 parent 332bf84 commit 360bbc6
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 13 deletions.
33 changes: 21 additions & 12 deletions src/arithmetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export function toWord(n: number) {


// -----------------------------------------------------------------------------
// Rounding, Decimals and Decimals
// Rounding, Decimals and Fractions

/** Returns the digits of a number n. */
export function digits(n: number) {
Expand All @@ -209,21 +209,30 @@ export function roundTo(n: number, increment = 1) {
* Returns an [numerator, denominator] array that approximated a `decimal` to
* `precision`. See http://en.wikipedia.org/wiki/Continued_fraction
*/
export function toFraction(decimal: number, precision = PRECISION) {
let n = [1, 0]; let d = [0, 1];
let a = Math.floor(decimal);
let rem = decimal - a;

while (d[0] <= 1 / precision) {
if (nearlyEquals(n[0] / d[0], precision)) return [n[0], d[0]];
export function toFraction(x: number, maxDen = 1000, precision = 1e-12): [num: number, den: number] | undefined {
let n = [1, 0];
let d = [0, 1];
const absX = Math.abs(x);
let rem = absX;

while (Math.abs(n[0] / d[0] - absX) > precision) {
const a = Math.floor(rem);
n = [a * n[0] + n[1], n[0]];
d = [a * d[0] + d[1], d[0]];
a = Math.floor(1 / rem);
rem = 1 / rem - a;
if (d[0] > maxDen) return;
rem = 1 / (rem - a);
}

// No nice rational representation so return an irrational "fraction"
return [decimal, 1];
// We get as close as we want with our tolerance, and if that fraction is still good past our computation we return it.
// Otherwise, we return false, meaning we didn't find a good enough rational approximation.
if (d[0] === 1 || !nearlyEquals(n[0] / d[0], absX, precision)) return;
return [sign(x) * n[0], d[0]];
}

export function toMixedNumber(x: number, maxDen?: number, precision?: number): [base: number, num: number, den: number] | undefined {
const base = Math.trunc(x);
const fraction = toFraction(Math.abs(x - base), maxDen, precision);
return (fraction) ? [base, ...fraction] : undefined;
}


Expand Down
19 changes: 18 additions & 1 deletion test/arithmetic-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


import tape from 'tape';
import {numberFormat, parseNumber, scientificFormat, toWord} from '../src';
import {numberFormat, parseNumber, scientificFormat, toFraction, toMixedNumber, toWord} from '../src';


tape('numberFormat', (test) => {
Expand Down Expand Up @@ -107,3 +107,20 @@ tape('parseNumber', (test) => {

test.end();
});


tape('fractions', (test) => {
test.deepEqual(toFraction(0.333333333333), [1, 3]);
test.deepEqual(toFraction(-0.333333333333), [-1, 3]);
test.deepEqual(toFraction(0.999999999999), undefined);
test.deepEqual(toFraction(0.833333333333), [5, 6]);
test.deepEqual(toFraction(0.171717171717), [17, 99]);

test.deepEqual(toFraction(0.123412341234), undefined);
test.deepEqual(toFraction(0.123412341234, 10000), [1234, 9999]);

test.deepEqual(toMixedNumber(1.333333333333), [1, 1, 3]);
test.deepEqual(toMixedNumber(-1.333333333333), [-1, 1, 3]);

test.end();
});

0 comments on commit 360bbc6

Please sign in to comment.