diff --git a/__tests__/number.test.ts b/__tests__/number.test.ts index fa3f2e4d..ac3d6294 100644 --- a/__tests__/number.test.ts +++ b/__tests__/number.test.ts @@ -1,5 +1,6 @@ import { convertNumberToRomanNumeral, + formatNumberToFraction, roundToString, roundToNumber, } from '@/utility/number'; @@ -17,6 +18,28 @@ describe('number', () => { expect(convertNumberToRomanNumeral(9)).toBe('IX'); expect(convertNumberToRomanNumeral(10)).toBe('X'); }); + it('formats decimals to fractions', () => { + expect(formatNumberToFraction(0.1666666)).toBe('+1/6'); + expect(formatNumberToFraction(0.25)).toBe('+1/4'); + expect(formatNumberToFraction(0.3333333)).toBe('+1/3'); + expect(formatNumberToFraction(0.5)).toBe('+1/2'); + expect(formatNumberToFraction(0.6666666)).toBe('+2/3'); + expect(formatNumberToFraction(0.75)).toBe('+3/4'); + expect(formatNumberToFraction(0.8333333)).toBe('+5/6'); + expect(formatNumberToFraction(1.6666666)).toBe('+1 2/3'); + expect(formatNumberToFraction(1.75)).toBe('+1 3/4'); + expect(formatNumberToFraction(10.75)).toBe('+10 3/4'); + expect(formatNumberToFraction(-0.1666666)).toBe('-1/6'); + expect(formatNumberToFraction(-0.25)).toBe('-1/4'); + expect(formatNumberToFraction(-0.3333333)).toBe('-1/3'); + expect(formatNumberToFraction(-0.5)).toBe('-1/2'); + expect(formatNumberToFraction(-0.6666666)).toBe('-2/3'); + expect(formatNumberToFraction(-0.75)).toBe('-3/4'); + expect(formatNumberToFraction(-0.8333333)).toBe('-5/6'); + expect(formatNumberToFraction(-1.6666666)).toBe('-1 2/3'); + expect(formatNumberToFraction(-1.75)).toBe('-1 3/4'); + expect(formatNumberToFraction(-10.75)).toBe('-10 3/4'); + }); describe('rounds to a', () => { it('string', () => { expect(roundToString(1.2345, 1)).toBe('1.2'); diff --git a/src/utility/number.ts b/src/utility/number.ts index f097c6f5..dc48b4f6 100644 --- a/src/utility/number.ts +++ b/src/utility/number.ts @@ -21,14 +21,22 @@ const gcd = (a: number, b: number): number => { } }; +const FRACTION_TOLERANCE = 0.011; +const STICKY_DECIMALS = [0.25, 0.33, 0.5, 0.66, 0.75]; +const MAX_FRACTION_LENGTH = 4; // Permit 1/64 but not 1/100 + const formatDecimalToFraction = (_decimal: number) => { // Prevent imprecision which causes numbers such as, // 0.1 to equal 0.10000000000000009 const decimal = parseFloat(_decimal.toPrecision(8)); - if (Math.abs(Math.abs(decimal) - 0.33) < 0.011) { + if (Math.abs(Math.abs(decimal) - 0.167) <= FRACTION_TOLERANCE) { + return '1/6'; + } else if (Math.abs(Math.abs(decimal) - 0.333) <= FRACTION_TOLERANCE) { return '1/3'; - } else if (Math.abs(Math.abs(decimal) - 0.66) <= 0.011) { + } else if (Math.abs(Math.abs(decimal) - 0.667) <= FRACTION_TOLERANCE) { return '2/3'; + } else if (Math.abs(Math.abs(decimal) - 0.833) <= FRACTION_TOLERANCE) { + return '5/6'; } else { const length = decimal.toString().length - 2; @@ -44,19 +52,15 @@ const formatDecimalToFraction = (_decimal: number) => { } }; -const STICKY_THRESHOLD = 0.011; -const STICKY_DECIMALS = [0.25, 0.33, 0.5, 0.66, 0.75]; -const MAX_FRACTION_LENGTH = 4; // Permit 1/64 but not 1/100 - export const formatNumberToFraction = (number: number) => { const sign = number >= 0 ? '+' : '-'; - let decimal = (1 - Math.abs(number % 1)) > STICKY_THRESHOLD + let decimal = (1 - Math.abs(number % 1)) > FRACTION_TOLERANCE ? number % 1 : 0; if (decimal !== 0) { for (const stickyDecimal of STICKY_DECIMALS) { - if (Math.abs(Math.abs(decimal) - stickyDecimal) < STICKY_THRESHOLD) { + if (Math.abs(Math.abs(decimal) - stickyDecimal) < FRACTION_TOLERANCE) { decimal = decimal < 0 ? -stickyDecimal : stickyDecimal; break; }