@mathigon/fermat
Advanced tools
Comparing version 1.1.11 to 1.1.12
@@ -36,8 +36,2 @@ /** Checks if two numbers are nearly equals. */ | ||
export declare function roundTo(n: number, increment?: number): number; | ||
/** | ||
* Returns an [numerator, denominator] array that approximated a `decimal` to | ||
* `precision`. See http://en.wikipedia.org/wiki/Continued_fraction | ||
*/ | ||
export declare function toFraction(x: number, maxDen?: number, precision?: number): [num: number, den: number] | undefined; | ||
export declare function toMixedNumber(x: number, maxDen?: number, precision?: number): [base: number, num: number, den: number] | undefined; | ||
/** Bounds a number between a lower and an upper limit. */ | ||
@@ -44,0 +38,0 @@ export declare function clamp(x: number, min?: number, max?: number): number; |
@@ -67,4 +67,2 @@ "use strict"; | ||
subsets: () => subsets, | ||
toFraction: () => toFraction, | ||
toMixedNumber: () => toMixedNumber, | ||
toOrdinal: () => toOrdinal, | ||
@@ -247,24 +245,2 @@ toWord: () => toWord, | ||
} | ||
function toFraction(x, maxDen = 1e3, precision = 1e-12) { | ||
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]]; | ||
if (d[0] > maxDen) | ||
return; | ||
rem = 1 / (rem - a); | ||
} | ||
if (d[0] === 1 || !nearlyEquals(n[0] / d[0], absX, precision)) | ||
return; | ||
return [sign(x) * n[0], d[0]]; | ||
} | ||
function toMixedNumber(x, maxDen, precision) { | ||
const base = Math.trunc(x); | ||
const fraction = toFraction(Math.abs(x - base), maxDen, precision); | ||
return fraction ? [base, ...fraction] : void 0; | ||
} | ||
function clamp(x, min = -Infinity, max = Infinity) { | ||
@@ -567,2 +543,3 @@ return Math.min(max, Math.max(min, x)); | ||
var FORMAT = /^([0-9\-.]*)([%πkmbtq]?)(\/([0-9\-.]+))?([%π]?)$/; | ||
var tooBig = (x) => x >= Number.MAX_SAFE_INTEGER; | ||
var XNumber = class _XNumber { | ||
@@ -579,7 +556,37 @@ /** Only used for fractions and always ≥ 0. */ | ||
} | ||
toMixed() { | ||
if (!this.den || this.unit) | ||
return this.toString(); | ||
const part = Math.abs(this.num) % this.den; | ||
const whole = Math.abs(Math.trunc(this.value)); | ||
if (!whole) | ||
return this.toString(); | ||
return `${this.sign < 0 ? "\u2013" : ""}${whole} ${part}/${this.den}`; | ||
} | ||
toExpr(type, precision = 4) { | ||
const v = this.value; | ||
if (Math.abs(v) >= Number.MAX_VALUE) | ||
return "\u221E"; | ||
if (tooBig(this.num) || this.den && tooBig(this.den)) | ||
type = "decimal"; | ||
if (type === "scientific" || Math.abs(v) >= Number.MAX_SAFE_INTEGER) { | ||
const [base, power2] = this.value.toExponential(precision - 1).split("e"); | ||
if (Math.abs(+power2) >= precision) { | ||
const isNeg = power2.startsWith("-"); | ||
const exp = `${isNeg ? "(" : ""}${isNeg ? power2 : power2.slice(1)}${isNeg ? ")" : ""}`; | ||
return `${base.replace(/\.?0+$/, "")} \xD7 10^${exp}${this.unit || ""}`; | ||
} | ||
} | ||
if (!this.unit && !this.den || type === "decimal" || type === "scientific") { | ||
const formatted = numberFormat(this.value, precision); | ||
return formatted.match(/^[\d.]+$/g) ? formatted : `"${formatted}"`; | ||
} else { | ||
return type === "mixed" ? this.toMixed() : this.toString(); | ||
} | ||
} | ||
toString(precision = 4) { | ||
const separators = !this.den && !this.unit; | ||
let num = numberFormat(this.num, precision, separators); | ||
let num = numberFormat(this.num, this.den ? 0 : precision, separators); | ||
let unit = this.unit || ""; | ||
const den = this.den ? `/${numberFormat(this.den, precision, separators)}` : ""; | ||
const den = this.den ? `/${numberFormat(this.den, 0, separators)}` : ""; | ||
if (num === "0") | ||
@@ -628,6 +635,11 @@ unit = ""; | ||
} | ||
get fraction() { | ||
if (this.unit || !isInteger(this.num)) | ||
return; | ||
return [this.num, this.den || 1]; | ||
} | ||
// --------------------------------------------------------------------------- | ||
/** Parses a number string, e.g. '1/2' or '20.7%'. */ | ||
static fromString(s) { | ||
s = s.toLowerCase().replace(/[\s,]/g, "").replace("\u2013", "-").replace("pi", "\u03C0"); | ||
s = s.toLowerCase().replace(/[\s,"]/g, "").replace("\u2013", "-").replace("pi", "\u03C0"); | ||
const match = s.match(FORMAT); | ||
@@ -657,29 +669,18 @@ if (!match) | ||
/** Converts a decimal into the closest fraction with a given maximum denominator. */ | ||
static fractionFromDecimal(x, maxDen = 100) { | ||
const sign2 = Math.sign(x); | ||
const whole = Math.floor(Math.abs(x)); | ||
x = Math.abs(x) - whole; | ||
let a = 0; | ||
let b = 1; | ||
let c = 1; | ||
let d = 1; | ||
while (b <= maxDen && d <= maxDen) { | ||
const mediant = (a + c) / (b + d); | ||
if (x === mediant) { | ||
if (b + d <= maxDen) { | ||
return new _XNumber(sign2 * (whole * (b + d) + a + c), b + d); | ||
} else if (d > b) { | ||
return new _XNumber(sign2 * (whole * d + c), d); | ||
} else { | ||
return new _XNumber(sign2 * (whole * b + a), b); | ||
} | ||
} else if (x > mediant) { | ||
[a, b] = [a + c, b + d]; | ||
} else { | ||
[c, d] = [a + c, b + d]; | ||
} | ||
static fractionFromDecimal(x, maxDen = 1e3, precision = 1e-12) { | ||
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]]; | ||
if (d[0] > maxDen) | ||
return new _XNumber(x); | ||
rem = 1 / (rem - a); | ||
} | ||
if (b > maxDen) | ||
return new _XNumber(sign2 * (whole * d + c), d); | ||
return new _XNumber(sign2 * (whole * b + a), b); | ||
if (!nearlyEquals(n[0] / d[0], absX, precision)) | ||
return new _XNumber(x); | ||
return new _XNumber(sign(x) * n[0], d[0] === 1 ? void 0 : d[0]); | ||
} | ||
@@ -686,0 +687,0 @@ // --------------------------------------------------------------------------- |
@@ -178,24 +178,2 @@ var __defProp = Object.defineProperty; | ||
} | ||
function toFraction(x, maxDen = 1e3, precision = 1e-12) { | ||
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]]; | ||
if (d[0] > maxDen) | ||
return; | ||
rem = 1 / (rem - a); | ||
} | ||
if (d[0] === 1 || !nearlyEquals(n[0] / d[0], absX, precision)) | ||
return; | ||
return [sign(x) * n[0], d[0]]; | ||
} | ||
function toMixedNumber(x, maxDen, precision) { | ||
const base = Math.trunc(x); | ||
const fraction = toFraction(Math.abs(x - base), maxDen, precision); | ||
return fraction ? [base, ...fraction] : void 0; | ||
} | ||
function clamp(x, min = -Infinity, max = Infinity) { | ||
@@ -498,2 +476,3 @@ return Math.min(max, Math.max(min, x)); | ||
var FORMAT = /^([0-9\-.]*)([%πkmbtq]?)(\/([0-9\-.]+))?([%π]?)$/; | ||
var tooBig = (x) => x >= Number.MAX_SAFE_INTEGER; | ||
var XNumber = class _XNumber { | ||
@@ -510,7 +489,37 @@ /** Only used for fractions and always ≥ 0. */ | ||
} | ||
toMixed() { | ||
if (!this.den || this.unit) | ||
return this.toString(); | ||
const part = Math.abs(this.num) % this.den; | ||
const whole = Math.abs(Math.trunc(this.value)); | ||
if (!whole) | ||
return this.toString(); | ||
return `${this.sign < 0 ? "\u2013" : ""}${whole} ${part}/${this.den}`; | ||
} | ||
toExpr(type, precision = 4) { | ||
const v = this.value; | ||
if (Math.abs(v) >= Number.MAX_VALUE) | ||
return "\u221E"; | ||
if (tooBig(this.num) || this.den && tooBig(this.den)) | ||
type = "decimal"; | ||
if (type === "scientific" || Math.abs(v) >= Number.MAX_SAFE_INTEGER) { | ||
const [base, power2] = this.value.toExponential(precision - 1).split("e"); | ||
if (Math.abs(+power2) >= precision) { | ||
const isNeg = power2.startsWith("-"); | ||
const exp = `${isNeg ? "(" : ""}${isNeg ? power2 : power2.slice(1)}${isNeg ? ")" : ""}`; | ||
return `${base.replace(/\.?0+$/, "")} \xD7 10^${exp}${this.unit || ""}`; | ||
} | ||
} | ||
if (!this.unit && !this.den || type === "decimal" || type === "scientific") { | ||
const formatted = numberFormat(this.value, precision); | ||
return formatted.match(/^[\d.]+$/g) ? formatted : `"${formatted}"`; | ||
} else { | ||
return type === "mixed" ? this.toMixed() : this.toString(); | ||
} | ||
} | ||
toString(precision = 4) { | ||
const separators = !this.den && !this.unit; | ||
let num = numberFormat(this.num, precision, separators); | ||
let num = numberFormat(this.num, this.den ? 0 : precision, separators); | ||
let unit = this.unit || ""; | ||
const den = this.den ? `/${numberFormat(this.den, precision, separators)}` : ""; | ||
const den = this.den ? `/${numberFormat(this.den, 0, separators)}` : ""; | ||
if (num === "0") | ||
@@ -559,6 +568,11 @@ unit = ""; | ||
} | ||
get fraction() { | ||
if (this.unit || !isInteger(this.num)) | ||
return; | ||
return [this.num, this.den || 1]; | ||
} | ||
// --------------------------------------------------------------------------- | ||
/** Parses a number string, e.g. '1/2' or '20.7%'. */ | ||
static fromString(s) { | ||
s = s.toLowerCase().replace(/[\s,]/g, "").replace("\u2013", "-").replace("pi", "\u03C0"); | ||
s = s.toLowerCase().replace(/[\s,"]/g, "").replace("\u2013", "-").replace("pi", "\u03C0"); | ||
const match = s.match(FORMAT); | ||
@@ -588,29 +602,18 @@ if (!match) | ||
/** Converts a decimal into the closest fraction with a given maximum denominator. */ | ||
static fractionFromDecimal(x, maxDen = 100) { | ||
const sign2 = Math.sign(x); | ||
const whole = Math.floor(Math.abs(x)); | ||
x = Math.abs(x) - whole; | ||
let a = 0; | ||
let b = 1; | ||
let c = 1; | ||
let d = 1; | ||
while (b <= maxDen && d <= maxDen) { | ||
const mediant = (a + c) / (b + d); | ||
if (x === mediant) { | ||
if (b + d <= maxDen) { | ||
return new _XNumber(sign2 * (whole * (b + d) + a + c), b + d); | ||
} else if (d > b) { | ||
return new _XNumber(sign2 * (whole * d + c), d); | ||
} else { | ||
return new _XNumber(sign2 * (whole * b + a), b); | ||
} | ||
} else if (x > mediant) { | ||
[a, b] = [a + c, b + d]; | ||
} else { | ||
[c, d] = [a + c, b + d]; | ||
} | ||
static fractionFromDecimal(x, maxDen = 1e3, precision = 1e-12) { | ||
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]]; | ||
if (d[0] > maxDen) | ||
return new _XNumber(x); | ||
rem = 1 / (rem - a); | ||
} | ||
if (b > maxDen) | ||
return new _XNumber(sign2 * (whole * d + c), d); | ||
return new _XNumber(sign2 * (whole * b + a), b); | ||
if (!nearlyEquals(n[0] / d[0], absX, precision)) | ||
return new _XNumber(x); | ||
return new _XNumber(sign(x) * n[0], d[0] === 1 ? void 0 : d[0]); | ||
} | ||
@@ -1263,4 +1266,2 @@ // --------------------------------------------------------------------------- | ||
subsets, | ||
toFraction, | ||
toMixedNumber, | ||
toOrdinal, | ||
@@ -1267,0 +1268,0 @@ toWord, |
@@ -9,2 +9,4 @@ type Suffix = '%' | 'π' | undefined; | ||
valueOf(): number; | ||
toMixed(): string; | ||
toExpr(type?: 'decimal' | 'fraction' | 'mixed' | 'scientific', precision?: number): string; | ||
toString(precision?: number): string; | ||
@@ -24,6 +26,7 @@ toMathML(): string; | ||
get negative(): XNumber; | ||
get fraction(): number[] | undefined; | ||
/** Parses a number string, e.g. '1/2' or '20.7%'. */ | ||
static fromString(s: string): XNumber | undefined; | ||
/** Converts a decimal into the closest fraction with a given maximum denominator. */ | ||
static fractionFromDecimal(x: number, maxDen?: number): XNumber; | ||
static fractionFromDecimal(x: number, maxDen?: number, precision?: number): XNumber; | ||
clamp(min?: number, max?: number): XNumber; | ||
@@ -30,0 +33,0 @@ add(a: XNumber | number): XNumber; |
{ | ||
"name": "@mathigon/fermat", | ||
"version": "1.1.11", | ||
"version": "1.1.12", | ||
"license": "MIT", | ||
@@ -38,12 +38,12 @@ "homepage": "https://mathigon.io/fermat", | ||
"dependencies": { | ||
"@mathigon/core": "1.1.12" | ||
"@mathigon/core": "1.1.13" | ||
}, | ||
"devDependencies": { | ||
"@types/tape": "5.6.0", | ||
"@typescript-eslint/eslint-plugin": "6.4.0", | ||
"@typescript-eslint/parser": "6.4.0", | ||
"esbuild": "0.19.2", | ||
"eslint": "8.48.0", | ||
"@types/tape": "5.6.1", | ||
"@typescript-eslint/eslint-plugin": "6.7.4", | ||
"@typescript-eslint/parser": "6.7.4", | ||
"esbuild": "0.19.4", | ||
"eslint": "8.50.0", | ||
"eslint-plugin-import": "2.28.1", | ||
"tape": "5.6.6", | ||
"tape": "5.7.0", | ||
"ts-node": "10.9.1", | ||
@@ -50,0 +50,0 @@ "tslib": "2.6.2", |
@@ -208,33 +208,3 @@ // ============================================================================ | ||
/** | ||
* Returns an [numerator, denominator] array that approximated a `decimal` to | ||
* `precision`. See http://en.wikipedia.org/wiki/Continued_fraction | ||
*/ | ||
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]]; | ||
if (d[0] > maxDen) return; | ||
rem = 1 / (rem - a); | ||
} | ||
// 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; | ||
} | ||
// ----------------------------------------------------------------------------- | ||
@@ -241,0 +211,0 @@ // Simple Operations |
@@ -7,3 +7,3 @@ // ============================================================================= | ||
import {isInteger, nearlyEquals, numberFormat} from './arithmetic'; | ||
import {isInteger, nearlyEquals, numberFormat, sign} from './arithmetic'; | ||
import {gcd, lcm} from './number-theory'; | ||
@@ -13,2 +13,3 @@ | ||
const FORMAT = /^([0-9\-.]*)([%πkmbtq]?)(\/([0-9\-.]+))?([%π]?)$/; | ||
const tooBig = (x: number) => x >= Number.MAX_SAFE_INTEGER; | ||
type Suffix = '%'|'π'|undefined; | ||
@@ -32,7 +33,40 @@ | ||
toMixed() { | ||
if (!this.den || this.unit) return this.toString(); | ||
const part = Math.abs(this.num) % this.den; | ||
const whole = Math.abs(Math.trunc(this.value)); | ||
if (!whole) return this.toString(); | ||
return `${this.sign < 0 ? '–' : ''}${whole} ${part}/${this.den}`; | ||
} | ||
toExpr(type?: 'decimal'|'fraction'|'mixed'|'scientific', precision = 4) { | ||
const v = this.value; | ||
// TODO Decide if we really want to return infinity here... | ||
if (Math.abs(v) >= Number.MAX_VALUE) return '∞'; | ||
if (tooBig(this.num) || this.den && tooBig(this.den)) type = 'decimal'; | ||
// In scientific notation, we try to return a number in the form a × 10^b | ||
if (type === 'scientific' || Math.abs(v) >= Number.MAX_SAFE_INTEGER) { | ||
const [base, power] = this.value.toExponential(precision - 1).split('e'); | ||
if (Math.abs(+power) >= precision) { | ||
const isNeg = power.startsWith('-'); | ||
const exp = `${isNeg ? '(' : ''}${isNeg ? power : power.slice(1)}${isNeg ? ')' : ''}`; | ||
return `${base.replace(/\.?0+$/, '')} × 10^${exp}${this.unit || ''}`; | ||
} | ||
} | ||
if ((!this.unit && !this.den) || type === 'decimal' || type === 'scientific') { | ||
const formatted = numberFormat(this.value, precision); | ||
// For non-standard number formatting, we add quotes for expr parsing. | ||
return (formatted.match(/^[\d.]+$/g) ? formatted : `"${formatted}"`); | ||
} else { | ||
return type === 'mixed' ? this.toMixed() : this.toString(); | ||
} | ||
} | ||
toString(precision = 4) { | ||
const separators = !this.den && !this.unit; | ||
let num = numberFormat(this.num, precision, separators); | ||
let num = numberFormat(this.num, this.den ? 0 : precision, separators); | ||
let unit = this.unit || ''; | ||
const den = this.den ? `/${numberFormat(this.den, precision, separators)}` : ''; | ||
const den = this.den ? `/${numberFormat(this.den, 0, separators)}` : ''; | ||
if (num === '0') unit = ''; | ||
@@ -83,2 +117,7 @@ if (unit === 'π' && !this.den && (num === '1' || num === '–1')) num = num.replace('1', ''); | ||
get fraction() { | ||
if (this.unit || !isInteger(this.num)) return; | ||
return [this.num, this.den || 1]; | ||
} | ||
// --------------------------------------------------------------------------- | ||
@@ -88,3 +127,3 @@ | ||
static fromString(s: string) { | ||
s = s.toLowerCase().replace(/[\s,]/g, '').replace('–', '-').replace('pi', 'π'); | ||
s = s.toLowerCase().replace(/[\s,"]/g, '').replace('–', '-').replace('pi', 'π'); | ||
const match = s.match(FORMAT); | ||
@@ -116,31 +155,18 @@ if (!match) return; | ||
/** Converts a decimal into the closest fraction with a given maximum denominator. */ | ||
static fractionFromDecimal(x: number, maxDen = 100) { | ||
const sign = Math.sign(x); | ||
const whole = Math.floor(Math.abs(x)); | ||
x = Math.abs(x) - whole; | ||
static fractionFromDecimal(x: number, maxDen = 1000, precision = 1e-12) { | ||
let n = [1, 0]; | ||
let d = [0, 1]; | ||
const absX = Math.abs(x); | ||
let rem = absX; | ||
let a = 0; | ||
let b = 1; | ||
let c = 1; | ||
let d = 1; | ||
while (b <= maxDen && d <= maxDen) { | ||
const mediant = (a + c) / (b + d); | ||
if (x === mediant) { | ||
if (b + d <= maxDen) { | ||
return new XNumber(sign * (whole * (b + d) + a + c), b + d); | ||
} else if (d > b) { | ||
return new XNumber(sign * (whole * d + c), d); | ||
} else { | ||
return new XNumber(sign * (whole * b + a), b); | ||
} | ||
} else if (x > mediant) { | ||
[a, b] = [a + c, b + d]; | ||
} else { | ||
[c, d] = [a + c, b + d]; | ||
} | ||
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]]; | ||
if (d[0] > maxDen) return new XNumber(x); | ||
rem = 1 / (rem - a); | ||
} | ||
if (b > maxDen) return new XNumber(sign * (whole * d + c), d); | ||
return new XNumber(sign * (whole * b + a), b); | ||
if (!nearlyEquals(n[0] / d[0], absX, precision)) return new XNumber(x); | ||
return new XNumber(sign(x) * n[0], d[0] === 1 ? undefined : d[0]); | ||
} | ||
@@ -147,0 +173,0 @@ |
@@ -8,3 +8,3 @@ // ============================================================================= | ||
import tape from 'tape'; | ||
import {numberFormat, parseNumber, scientificFormat, toFraction, toMixedNumber, toWord} from '../src'; | ||
import {numberFormat, parseNumber, scientificFormat, toWord} from '../src'; | ||
@@ -111,18 +111,1 @@ | ||
}); | ||
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(); | ||
}); |
@@ -14,2 +14,3 @@ // ============================================================================= | ||
const dec = (s: number) => XNumber.fractionFromDecimal(s)?.toString(); | ||
const expr = (s: number, type: 'decimal'|'fraction'|'mixed'|'scientific') => XNumber.fractionFromDecimal(s)?.toExpr(type); | ||
@@ -24,3 +25,3 @@ tape('XNumber Constructors', (test) => { | ||
test.equal(n(0, 2, 'π').toString(), '0'); | ||
test.equal(n(10000, 20000).toString(), '10k/20k'); | ||
test.equal(n(10000, 20000).toString(), '10000/20000'); | ||
test.end(); | ||
@@ -110,8 +111,34 @@ }); | ||
test.equal(dec(0.33), '33/100'); | ||
test.equal(dec(0.333), '1/3'); | ||
test.equal(dec(0.0333), '1/30'); | ||
test.equal(dec(0.333), '333/1000'); | ||
test.equal(dec(0.333333333333), '1/3'); | ||
test.equal(dec(0.0333333333333), '1/30'); | ||
test.equal(dec(0.05), '1/20'); | ||
test.equal(dec(0.04761904762), '1/21'); | ||
test.equal(dec(-5), '–5'); | ||
test.equal(dec(0.333333333333), '1/3'); | ||
test.equal(dec(-0.333333333333), '–1/3'); | ||
test.equal(dec(0.999999999999), '1'); | ||
test.equal(dec(0.833333333333), '5/6'); | ||
test.equal(dec(0.171717171717), '17/99'); | ||
test.end(); | ||
}); | ||
tape('Mixed numbers', (test) => { | ||
test.deepEqual(expr(1.999999999999, 'mixed'), '2'); | ||
test.deepEqual(expr(0.333333333333, 'mixed'), '1/3'); | ||
test.deepEqual(expr(1.333333333333, 'mixed'), '1 1/3'); | ||
test.deepEqual(expr(-1.333333333333, 'mixed'), '–1 1/3'); | ||
test.end(); | ||
}); | ||
tape('Scientific notation', (test) => { | ||
test.deepEqual(expr(0.000002, 'scientific'), '2 × 10^(-6)'); | ||
test.deepEqual(expr(0.00012, 'scientific'), '1.2 × 10^(-4)'); | ||
test.deepEqual(expr(1234.3, 'scientific'), '"1,234"'); | ||
test.deepEqual(expr(12343.2, 'decimal'), '"12.3k"'); | ||
test.deepEqual(expr(12343.2, 'scientific'), '1.234 × 10^4'); | ||
test.deepEqual(expr(123432.1, 'scientific'), '1.234 × 10^5'); | ||
test.end(); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
311930
4465
+ Added@mathigon/core@1.1.13(transitive)
- Removed@mathigon/core@1.1.12(transitive)
Updated@mathigon/core@1.1.13