Add 1/6 fractions, add test coverage

This commit is contained in:
Sam Becker 2025-03-28 11:31:29 -05:00
parent 13a9e855bc
commit ed383ae56d
2 changed files with 35 additions and 8 deletions

View File

@ -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');

View File

@ -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;
}