Skip to content
markpaper

src/account/decimal.test.ts

v0.3.0 · 2.4 KB

Download file
import { describe, expect, it } from 'vitest';
import * as D from './decimal.js';

const s = (x: string) => {
  const d = D.parseDec(x);
  if (!d) throw new Error(`bad fixture ${x}`);
  return d;
};

describe('decimal (private exact arithmetic)', () => {
  it('accepts strict decimal strings only', () => {
    for (const ok of ['0', '1', '-0.5', '123.4500', '0.00000001', '-999999.99999999']) {
      expect(D.parseDec(ok)).not.toBeNull();
    }
    for (const bad of [' 100', '100 ', 'abc', '1e5', '+1', '1.', '.5', '', '--1', '1,5', 'NaN', 'Infinity']) {
      expect(D.parseDec(bad)).toBeNull();
    }
    expect(D.parseDec(1)).toBeNull();
    expect(D.parseDec(null)).toBeNull();
    expect(D.parseDec(undefined)).toBeNull();
  });

  it('adds without binary float error', () => {
    expect(D.toDecString(D.add(s('0.1'), s('0.2')))).toBe('0.3');
    expect(D.toNumber(D.add(s('0.1'), s('0.2')))).toBe(0.3);
    let acc = D.ZERO;
    for (let i = 0; i < 10; i++) acc = D.add(acc, s('0.1'));
    expect(D.toDecString(acc)).toBe('1');
  });

  it('subtracts, compares and takes max/abs/sign across scales', () => {
    expect(D.toDecString(D.sub(s('1000'), s('200.25')))).toBe('799.75');
    expect(D.cmp(s('1.10'), s('1.1'))).toBe(0);
    expect(D.cmp(s('-2'), s('1.5'))).toBe(-1);
    expect(D.cmp(s('2'), s('1.99999999'))).toBe(1);
    expect(D.toDecString(D.max(D.ZERO, s('-3')))).toBe('0');
    expect(D.toDecString(D.abs(s('-3.5')))).toBe('3.5');
    expect(D.sign(s('-0.0'))).toBe(0);
    expect(D.sign(s('-0.01'))).toBe(-1);
    expect(D.isZero(s('0.000'))).toBe(true);
  });

  it('prints canonical strings', () => {
    expect(D.toDecString(s('100.500'))).toBe('100.5');
    expect(D.toDecString(s('-0.000'))).toBe('0');
    expect(D.toDecString(s('0.05'))).toBe('0.05');
    expect(D.toDecString(s('-12.340'))).toBe('-12.34');
  });

  it('shifts left exactly (rate to bps)', () => {
    expect(D.toDecString(D.shiftLeft(s('0.00015'), 4))).toBe('1.5');
    expect(D.toDecString(D.shiftLeft(s('-0.00003'), 4))).toBe('-0.3');
    expect(D.toDecString(D.shiftLeft(s('2'), 4))).toBe('20000');
    expect(() => D.shiftLeft(s('1'), -1)).toThrow(RangeError);
  });

  it('converts finite numbers with bounded scale', () => {
    expect(D.toDecString(D.decFromNumber(12.5) as D.Dec)).toBe('12.5');
    expect(D.decFromNumber(Number.NaN)).toBeNull();
    expect(D.decFromNumber(Number.POSITIVE_INFINITY)).toBeNull();
  });
});
All files