src/numbers/numbers.test.ts
v0.1.0 · 1.7 KB
import { describe, expect, it } from 'vitest';
import {
decAdd,
decFromNumber,
decToString,
isOnStep,
parseDec,
parseLossless,
snapToGrid,
snapToStep,
stepsOf,
} from './index.js';
describe('exact decimals and lossless JSON', () => {
it('preserves numeric tokens and escaped string contents', () => {
expect(parseLossless('{"id":9007199254741009,"quantity":1.500,"small":2e-9,"text":"1.5 \\" token"}')).toEqual({
id: '9007199254741009',
quantity: '1.500',
small: '2e-9',
text: '1.5 " token',
});
});
it.each(['{1:2}', '{"a":01}', '[1e]', '[1.2.3]'])('rejects invalid JSON %s', (text) => {
expect(() => parseLossless(text)).toThrow();
});
it('does not lose an exact lot to binary division', () => {
expect(stepsOf(parseDec('0.57')!, parseDec('0.01')!, 'floor')).toBe(57n);
expect(decToString(snapToStep(decFromNumber(0.5799999999999998)!, parseDec('0.02')!, 'floor', 'float'))).toBe(
'0.58',
);
});
it('rounds negative halves away from zero and remains on grid', () => {
const step = parseDec('0.2')!;
const value = snapToStep(parseDec('-0.7')!, step, 'nearest');
expect(decToString(value)).toBe('-0.8');
expect(isOnStep(value, step)).toBe(true);
});
it('adds different scales exactly and rejects abusive exponents', () => {
expect(decToString(decAdd(parseDec('7.20')!, parseDec('0.008')!))).toBe('7.208');
expect(parseDec('1e9999')).toBeNull();
});
it('distinguishes double print noise from a real off-grid position', () => {
expect(snapToGrid(parseDec('0.38000000000000006')!, parseDec('0.02')!).noise).toBe(true);
expect(snapToGrid(parseDec('0.387')!, parseDec('0.02')!).noise).toBe(false);
});
});