UNPKG

@hastom/fixed-point

Version:

Light lib for fixed point math made around native bigint

1,260 lines (1,127 loc) 46.5 kB
import { Decimals, FixedPoint, Rounding } from './FixedPoint' import { fpFromDecimal, fpFromInt } from './parsers' describe('fixed-point', () => { it('must create instance', () => { const n = new FixedPoint(1_000n, 3n) expect(n.base).toBe(1_000n) expect(n.precision).toBe(3n) expect(n.toDecimalString()).toBe('1.000') }) describe('decimal parser', () => { it('must create instance from decimal number = 1', () => { const n = fpFromDecimal(1, 5) expect(n.base).toBe(1_00000n) expect(n.precision).toBe(5n) }) it('must create instance from decimal number < 1', () => { const n = fpFromDecimal(0.00001, 5) expect(n.base).toBe(1n) expect(n.precision).toBe(5n) }) it('must create instance from decimal number > 1', () => { const n = fpFromDecimal(1.123123, 5) expect(n.base).toBe(1_12312n) expect(n.precision).toBe(5n) }) it('must create instance from decimal string = 1', () => { const n = fpFromDecimal('1', 5) expect(n.base).toBe(1_00000n) expect(n.precision).toBe(5n) }) it('must create instance from decimal string < 1', () => { const n = fpFromDecimal('0.00001', 5) expect(n.base).toBe(1n) expect(n.precision).toBe(5n) }) it('must create instance from decimal string > 1', () => { const n = fpFromDecimal('1.12312', 5) expect(n.base).toBe(1_12312n) expect(n.precision).toBe(5n) }) it('must create instance from decimal string < 0', () => { const n = fpFromDecimal('-1.12312', 5) expect(n.base).toBe(-1_12312n) expect(n.precision).toBe(5n) }) it('must create instance from decimal bigint = 1', () => { const n = fpFromDecimal(1n, 5) expect(n.base).toBe(1_00000n) expect(n.precision).toBe(5n) }) it('must create instance from decimal bigint > 1', () => { const n = fpFromDecimal(123n, 5) expect(n.base).toBe(123_00000n) expect(n.precision).toBe(5n) }) it('must create instance from decimal bigint < 0', () => { const n = fpFromDecimal(-12, 5) expect(n.base).toBe(-12_00000n) expect(n.precision).toBe(5n) }) }) describe('int parser', () => { describe('number src', () => { it('must create instance from int number (base = 1, src = 9, dst = 5)', () => { const n = fpFromInt(1_000_000_000, 9, 5) expect(n.base).toBe(1_00000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('1.00000') }) it('must create instance from int number (base = 0.33, src = 9, dst = 5)', () => { const n = fpFromInt(330_000_000, 9, 5) expect(n.base).toBe(33000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('0.33000') }) it('must create instance from int number (base = 2.55, src = 9, dst = 5)', () => { const n = fpFromInt(2_550_000_000, 9, 5) expect(n.base).toBe(255000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('2.55000') }) it('must create instance from int number (base = 1, src = 5, dst = 9)', () => { const n = fpFromInt(1_00000, 5, 9) expect(n.base).toBe(1_000_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('1.000000000') }) it('must create instance from int number (base = 0.33, src = 5, dst = 9)', () => { const n = fpFromInt(33000, 5, 9) expect(n.base).toBe(330_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('0.330000000') }) it('must create instance from int number (base = 2.55, src = 5, dst = 9)', () => { const n = fpFromInt(2_55000, 5, 9) expect(n.base).toBe(2_550_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('2.550000000') }) }) describe('string src', () => { it('must create instance from int string (base = 1, src = 9, dst = 5)', () => { const n = fpFromInt('1000000000', 9, 5) expect(n.base).toBe(1_00000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('1.00000') }) it('must create instance from int string (base = 0.33, src = 9, dst = 5)', () => { const n = fpFromInt('330000000', 9, 5) expect(n.base).toBe(33000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('0.33000') }) it('must create instance from int string (base = 2.55, src = 9, dst = 5)', () => { const n = fpFromInt('2550000000', 9, 5) expect(n.base).toBe(255000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('2.55000') }) it('must create instance from int string (base = 1, src = 5, dst = 9)', () => { const n = fpFromInt('100000', 5, 9) expect(n.base).toBe(1_000_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('1.000000000') }) it('must create instance from int string (base = 0.33, src = 5, dst = 9)', () => { const n = fpFromInt('33000', 5, 9) expect(n.base).toBe(330_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('0.330000000') }) it('must create instance from int string (base = 2.55, src = 5, dst = 9)', () => { const n = fpFromInt('255000', 5, 9) expect(n.base).toBe(2_550_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('2.550000000') }) }) describe('bigint src', () => { it('must create instance from int bigint (base = 1, src = 9, dst = 5)', () => { const n = fpFromInt(1_000_000_000n, 9, 5) expect(n.base).toBe(1_00000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('1.00000') }) it('must create instance from int bigint (base = 0.33, src = 9, dst = 5)', () => { const n = fpFromInt(330_000_000n, 9, 5) expect(n.base).toBe(33000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('0.33000') }) it('must create instance from int bigint (base = 2.55, src = 9, dst = 5)', () => { const n = fpFromInt(2_550_000_000n, 9, 5) expect(n.base).toBe(255000n) expect(n.precision).toBe(5n) expect(n.toDecimalString()).toBe('2.55000') }) it('must create instance from int bigint (base = 1, src = 5, dst = 9)', () => { const n = fpFromInt(1_00000n, 5, 9) expect(n.base).toBe(1_000_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('1.000000000') }) it('must create instance from int bigint (base = 0.33, src = 5, dst = 9)', () => { const n = fpFromInt(33000n, 5, 9) expect(n.base).toBe(330_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('0.330000000') }) it('must create instance from int bigint (base = 2.55, src = 5, dst = 9)', () => { const n = fpFromInt(2_55000n, 5, 9) expect(n.base).toBe(2_550_000_000n) expect(n.precision).toBe(9n) expect(n.toDecimalString()).toBe('2.550000000') }) }) }) describe('compare', () => { describe('=', () => { it('must compare = (p1 < p2), positive', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) expect(a.eq(b)).toBe(true) }) it('must compare = (p1 < p2), negative', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.220001', 9) expect(a.eq(b)).toBe(false) }) it('must compare = (p1 = p2), positive', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 9) expect(a.eq(b)).toBe(true) }) it('must compare = (p1 = p2), negative', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.220001', 9) expect(a.eq(b)).toBe(false) }) it('must compare = (p1 > p2), positive', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 6) expect(a.eq(b)).toBe(true) }) it('must compare = (p1 > p2), negative', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('-1', 6) expect(a.eq(b)).toBe(false) }) }) describe('>', () => { it('must compare > (p1 < p2), positive', () => { const a = fpFromDecimal('1.2203', 6) const b = fpFromDecimal('1.2202', 9) expect(a.gt(b)).toBe(true) }) it('must compare > (p1 < p2), negative', () => { const a = fpFromDecimal('1.220001', 6) const b = fpFromDecimal('1.220001', 9) expect(a.gt(b)).toBe(false) }) it('must compare > (p1 = p2), positive', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('-1.22', 9) expect(a.gt(b)).toBe(true) }) it('must compare > (p1 = p2), negative', () => { const a = fpFromDecimal('1.220001', 9) const b = fpFromDecimal('1.220002', 9) expect(a.gt(b)).toBe(false) }) it('must compare > (p1 > p2), positive', () => { const a = fpFromDecimal('2.22', 9) const b = fpFromDecimal('1.22', 6) expect(a.gt(b)).toBe(true) }) it('must compare > (p1 > p2), negative', () => { const a = fpFromDecimal('0.66', 9) const b = fpFromDecimal('0.77', 6) expect(a.gt(b)).toBe(false) }) }) describe('<', () => { it('must compare < (p1 < p2), positive', () => { const a = fpFromDecimal('0.22', 6) const b = fpFromDecimal('1.22', 9) expect(a.lt(b)).toBe(true) }) it('must compare < (p1 < p2), negative', () => { const a = fpFromDecimal('1.221233', 6) const b = fpFromDecimal('1.220001', 9) expect(a.lt(b)).toBe(false) }) it('must compare < (p1 = p2), positive', () => { const a = fpFromDecimal('1.2', 9) const b = fpFromDecimal('1.22', 9) expect(a.lt(b)).toBe(true) }) it('must compare < (p1 = p2), negative', () => { const a = fpFromDecimal('100.22', 9) const b = fpFromDecimal('1.220001', 9) expect(a.lt(b)).toBe(false) }) it('must compare < (p1 > p2), positive', () => { const a = fpFromDecimal('1.20', 9) const b = fpFromDecimal('1.22', 6) expect(a.lt(b)).toBe(true) }) it('must compare < (p1 > p2), negative', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('-1', 6) expect(a.lt(b)).toBe(false) }) }) describe('>=', () => { it('must compare >= (p1 < p2), positive', () => { const a = fpFromDecimal('1.2202', 6) const b = fpFromDecimal('1.2202', 9) expect(a.gte(b)).toBe(true) }) it('must compare >= (p1 < p2), negative', () => { const a = fpFromDecimal('1.0', 6) const b = fpFromDecimal('1.220001', 9) expect(a.gte(b)).toBe(false) }) it('must compare >= (p1 = p2), positive', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('-1.22', 9) expect(a.gte(b)).toBe(true) }) it('must compare >= (p1 = p2), negative', () => { const a = fpFromDecimal('1.220001', 9) const b = fpFromDecimal('1.220002', 9) expect(a.gte(b)).toBe(false) }) it('must compare >= (p1 > p2), positive', () => { const a = fpFromDecimal('2.22', 9) const b = fpFromDecimal('1.22', 6) expect(a.gte(b)).toBe(true) }) it('must compare >= (p1 > p2), negative', () => { const a = fpFromDecimal('0.66', 9) const b = fpFromDecimal('0.77', 6) expect(a.gte(b)).toBe(false) }) }) describe('<=', () => { it('must compare <= (p1 < p2), positive', () => { const a = fpFromDecimal('0.22', 6) const b = fpFromDecimal('1.22', 9) expect(a.lte(b)).toBe(true) }) it('must compare <= (p1 < p2), negative', () => { const a = fpFromDecimal('1.221233', 6) const b = fpFromDecimal('1.220001', 9) expect(a.lte(b)).toBe(false) }) it('must compare <= (p1 = p2), positive', () => { const a = fpFromDecimal('1.2', 9) const b = fpFromDecimal('1.22', 9) expect(a.lte(b)).toBe(true) }) it('must compare <= (p1 = p2), negative', () => { const a = fpFromDecimal('100.22', 9) const b = fpFromDecimal('1.220001', 9) expect(a.lte(b)).toBe(false) }) it('must compare <= (p1 > p2), positive', () => { const a = fpFromDecimal('1.20', 9) const b = fpFromDecimal('1.22', 6) expect(a.lte(b)).toBe(true) }) it('must compare <= (p1 > p2), negative', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('-1', 6) expect(a.lte(b)).toBe(false) }) }) describe('= 0', () => { it('must compare = 0, positive', () => { const a = fpFromDecimal('0.00', 6) expect(a.isZero()).toBe(true) }) it('must compare = 0, negative', () => { const a = fpFromDecimal('0.000001', 6) expect(a.isZero()).toBe(false) }) }) describe('> 0', () => { it('must compare > 0, positive', () => { const a = fpFromDecimal('0.00001', 6) expect(a.isPositive()).toBe(true) }) it('must compare > 0, negative', () => { const a = fpFromDecimal('0', 6) expect(a.isPositive()).toBe(false) }) }) describe('< 0', () => { it('must compare < 0, positive', () => { const a = fpFromDecimal('-0.00001', 6) expect(a.isNegative()).toBe(true) }) it('must compare < 0, negative', () => { const a = fpFromDecimal('1', 6) expect(a.isNegative()).toBe(false) }) }) }) describe('round', () => { // ROUND_UP tests it('must round up positive numbers', () => { const a = fpFromDecimal('2.4', 2) const result = a.round(Rounding.ROUND_UP) expect(result.toDecimalString()).toBe('3.00') const b = fpFromDecimal('2.5', 2) const resultB = b.round(Rounding.ROUND_UP) expect(resultB.toDecimalString()).toBe('3.00') const c = fpFromDecimal('2.1', 2) const resultC = c.round(Rounding.ROUND_UP) expect(resultC.toDecimalString()).toBe('3.00') }) it('must round up negative numbers', () => { const a = fpFromDecimal('-2.4', 2) const result = a.round(Rounding.ROUND_UP) expect(result.toDecimalString()).toBe('-3.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_UP) expect(resultB.toDecimalString()).toBe('-3.00') const c = fpFromDecimal('-2.1', 2) const resultC = c.round(Rounding.ROUND_UP) expect(resultC.toDecimalString()).toBe('-3.00') }) // ROUND_DOWN tests it('must round down positive numbers', () => { const a = fpFromDecimal('2.4', 2) const result = a.round(Rounding.ROUND_DOWN) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.5', 2) const resultB = b.round(Rounding.ROUND_DOWN) expect(resultB.toDecimalString()).toBe('2.00') const c = fpFromDecimal('2.9', 2) const resultC = c.round(Rounding.ROUND_DOWN) expect(resultC.toDecimalString()).toBe('2.00') }) it('must round down negative numbers', () => { const a = fpFromDecimal('-2.4', 2) const result = a.round(Rounding.ROUND_DOWN) expect(result.toDecimalString()).toBe('-2.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_DOWN) expect(resultB.toDecimalString()).toBe('-2.00') const c = fpFromDecimal('-2.9', 2) const resultC = c.round(Rounding.ROUND_DOWN) expect(resultC.toDecimalString()).toBe('-2.00') }) // ROUND_CEIL tests it('must round ceiling positive numbers', () => { const a = fpFromDecimal('2.4', 2) const result = a.round(Rounding.ROUND_CEIL) expect(result.toDecimalString()).toBe('3.00') const b = fpFromDecimal('2.5', 2) const resultB = b.round(Rounding.ROUND_CEIL) expect(resultB.toDecimalString()).toBe('3.00') const c = fpFromDecimal('2.0', 2) const resultC = c.round(Rounding.ROUND_CEIL) expect(resultC.toDecimalString()).toBe('2.00') }) it('must round ceiling negative numbers', () => { const a = fpFromDecimal('-2.4', 2) const result = a.round(Rounding.ROUND_CEIL) expect(result.toDecimalString()).toBe('-2.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_CEIL) expect(resultB.toDecimalString()).toBe('-2.00') const c = fpFromDecimal('-3.0', 2) const resultC = c.round(Rounding.ROUND_CEIL) expect(resultC.toDecimalString()).toBe('-3.00') }) // ROUND_FLOOR tests it('must round floor positive numbers', () => { const a = fpFromDecimal('2.4', 2) const result = a.round(Rounding.ROUND_FLOOR) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.5', 2) const resultB = b.round(Rounding.ROUND_FLOOR) expect(resultB.toDecimalString()).toBe('2.00') const c = fpFromDecimal('3.0', 2) const resultC = c.round(Rounding.ROUND_FLOOR) expect(resultC.toDecimalString()).toBe('3.00') }) it('must round floor negative numbers', () => { const a = fpFromDecimal('-2.4', 2) const result = a.round(Rounding.ROUND_FLOOR) expect(result.toDecimalString()).toBe('-3.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_FLOOR) expect(resultB.toDecimalString()).toBe('-3.00') const c = fpFromDecimal('-2.0', 2) const resultC = c.round(Rounding.ROUND_FLOOR) expect(resultC.toDecimalString()).toBe('-2.00') }) // ROUND_HALF_UP tests it('must round half up with exact .5 cases', () => { const a = fpFromDecimal('2.5', 2) const result = a.round(Rounding.ROUND_HALF_UP) expect(result.toDecimalString()).toBe('3.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_HALF_UP) expect(resultB.toDecimalString()).toBe('-3.00') }) it('must round half up with close to .5 cases', () => { const a = fpFromDecimal('2.49', 2) const result = a.round(Rounding.ROUND_HALF_UP) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.51', 2) const resultB = b.round(Rounding.ROUND_HALF_UP) expect(resultB.toDecimalString()).toBe('3.00') const c = fpFromDecimal('-2.49', 2) const resultC = c.round(Rounding.ROUND_HALF_UP) expect(resultC.toDecimalString()).toBe('-2.00') const d = fpFromDecimal('-2.51', 2) const resultD = d.round(Rounding.ROUND_HALF_UP) expect(resultD.toDecimalString()).toBe('-3.00') }) // ROUND_HALF_DOWN tests it('must round half down with exact .5 cases', () => { const a = fpFromDecimal('2.5', 2) const result = a.round(Rounding.ROUND_HALF_DOWN) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_HALF_DOWN) expect(resultB.toDecimalString()).toBe('-2.00') }) it('must round half down with close to .5 cases', () => { const a = fpFromDecimal('2.49', 2) const result = a.round(Rounding.ROUND_HALF_DOWN) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.51', 2) const resultB = b.round(Rounding.ROUND_HALF_DOWN) expect(resultB.toDecimalString()).toBe('3.00') const c = fpFromDecimal('-2.49', 2) const resultC = c.round(Rounding.ROUND_HALF_DOWN) expect(resultC.toDecimalString()).toBe('-2.00') const d = fpFromDecimal('-2.51', 2) const resultD = d.round(Rounding.ROUND_HALF_DOWN) expect(resultD.toDecimalString()).toBe('-3.00') }) // ROUND_HALF_EVEN tests it('must round half even with even neighbors', () => { const a = fpFromDecimal('2.5', 2) const result = a.round(Rounding.ROUND_HALF_EVEN) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_HALF_EVEN) expect(resultB.toDecimalString()).toBe('-2.00') }) it('must round half even with odd neighbors', () => { const a = fpFromDecimal('3.5', 2) const result = a.round(Rounding.ROUND_HALF_EVEN) expect(result.toDecimalString()).toBe('4.00') const b = fpFromDecimal('-3.5', 2) const resultB = b.round(Rounding.ROUND_HALF_EVEN) expect(resultB.toDecimalString()).toBe('-4.00') }) it('must round half even with close to .5 cases', () => { const a = fpFromDecimal('2.49', 2) const result = a.round(Rounding.ROUND_HALF_EVEN) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.51', 2) const resultB = b.round(Rounding.ROUND_HALF_EVEN) expect(resultB.toDecimalString()).toBe('3.00') }) // ROUND_HALF_CEIL tests it('must round half ceil with exact .5 cases', () => { const a = fpFromDecimal('2.5', 2) const result = a.round(Rounding.ROUND_HALF_CEIL) expect(result.toDecimalString()).toBe('3.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_HALF_CEIL) expect(resultB.toDecimalString()).toBe('-2.00') }) it('must round half ceil with close to .5 cases', () => { const a = fpFromDecimal('2.49', 2) const result = a.round(Rounding.ROUND_HALF_CEIL) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.51', 2) const resultB = b.round(Rounding.ROUND_HALF_CEIL) expect(resultB.toDecimalString()).toBe('3.00') const c = fpFromDecimal('-2.49', 2) const resultC = c.round(Rounding.ROUND_HALF_CEIL) expect(resultC.toDecimalString()).toBe('-2.00') const d = fpFromDecimal('-2.51', 2) const resultD = d.round(Rounding.ROUND_HALF_CEIL) expect(resultD.toDecimalString()).toBe('-3.00') }) // ROUND_HALF_FLOOR tests it('must round half floor with exact .5 cases', () => { const a = fpFromDecimal('2.5', 2) const result = a.round(Rounding.ROUND_HALF_FLOOR) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('-2.5', 2) const resultB = b.round(Rounding.ROUND_HALF_FLOOR) expect(resultB.toDecimalString()).toBe('-3.00') }) it('must round half floor with close to .5 cases', () => { const a = fpFromDecimal('2.49', 2) const result = a.round(Rounding.ROUND_HALF_FLOOR) expect(result.toDecimalString()).toBe('2.00') const b = fpFromDecimal('2.51', 2) const resultB = b.round(Rounding.ROUND_HALF_FLOOR) expect(resultB.toDecimalString()).toBe('3.00') const c = fpFromDecimal('-2.49', 2) const resultC = c.round(Rounding.ROUND_HALF_FLOOR) expect(resultC.toDecimalString()).toBe('-2.00') const d = fpFromDecimal('-2.51', 2) const resultD = d.round(Rounding.ROUND_HALF_FLOOR) expect(resultD.toDecimalString()).toBe('-3.00') }) // Edge cases it('must handle zero correctly across all rounding modes', () => { const zero = fpFromDecimal('0.0', 2) expect(zero.round(Rounding.ROUND_UP).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_DOWN).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_CEIL).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_FLOOR).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_HALF_UP).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_HALF_DOWN).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_HALF_EVEN).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_HALF_EVEN).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_HALF_CEIL).toDecimalString()).toBe('0.00') expect(zero.round(Rounding.ROUND_HALF_FLOOR).toDecimalString()).toBe('0.00') }) // Higher precision numbers test it('must handle higher precision numbers correctly', () => { const highPrecision = fpFromDecimal('3.141592', 6) // Round to integer expect(highPrecision.round(Rounding.ROUND_UP).toDecimalString()).toBe('4.000000') expect(highPrecision.round(Rounding.ROUND_DOWN).toDecimalString()).toBe('3.000000') expect(highPrecision.round(Rounding.ROUND_CEIL).toDecimalString()).toBe('4.000000') expect(highPrecision.round(Rounding.ROUND_FLOOR).toDecimalString()).toBe('3.000000') expect(highPrecision.round(Rounding.ROUND_HALF_UP).toDecimalString()).toBe('3.000000') expect(highPrecision.round(Rounding.ROUND_HALF_DOWN).toDecimalString()).toBe('3.000000') expect(highPrecision.round(Rounding.ROUND_HALF_EVEN).toDecimalString()).toBe('3.000000') expect(highPrecision.round(Rounding.ROUND_HALF_CEIL).toDecimalString()).toBe('3.000000') expect(highPrecision.round(Rounding.ROUND_HALF_FLOOR).toDecimalString()).toBe('3.000000') // Test a value with exact .5 in higher precision const halfPrecision = fpFromDecimal('3.500000', 6) expect(halfPrecision.round(Rounding.ROUND_HALF_UP).toDecimalString()).toBe('4.000000') expect(halfPrecision.round(Rounding.ROUND_HALF_DOWN).toDecimalString()).toBe('3.000000') expect(halfPrecision.round(Rounding.ROUND_HALF_EVEN).toDecimalString()).toBe('4.000000') expect(halfPrecision.round(Rounding.ROUND_HALF_CEIL).toDecimalString()).toBe('4.000000') expect(halfPrecision.round(Rounding.ROUND_HALF_FLOOR).toDecimalString()).toBe('3.000000') }) const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 9) const c = a.add(b) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) describe('convert precision', () => { it('must set precision higher', () => { const a = fpFromDecimal('1.22', 2) const b = a.toPrecision(9) expect(b.toDecimalString()).toBe('1.220000000') }) it('must set precision lower rounding down', () => { const a = fpFromDecimal('1.23456789', 8) const b = a.toPrecision(2) expect(b.toDecimalString()).toBe('1.23') }) it('must set precision lower rounding up > 1', () => { const a = fpFromDecimal('1.23456789', 8) const b = a.toPrecision(2, Rounding.ROUND_UP) expect(b.toDecimalString()).toBe('1.24') }) it('must set precision lower rounding down < 1', () => { const a = fpFromDecimal('0.23456789', 8) const b = a.toPrecision(0) expect(b.toDecimalString()).toBe('0') }) it('must set precision lower rounding up < 1', () => { const a = fpFromDecimal('0.23456789', 8) const b = a.toPrecision(0, Rounding.ROUND_UP) expect(b.toDecimalString()).toBe('1') }) }) describe('math', () => { describe('+', () => { it('must add (p1 > p2)', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.add(b) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 < p2)', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) const c = a.add(b) expect(c.precision).toBe(6n) expect(c.base).toBe(2_440_000n) expect(c.toDecimalString()).toBe('2.440000') }) it('must add (p1 = p2) force left', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.left) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 > p2) force left', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.add(b, Decimals.left) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 < p2) force left', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.left) expect(c.precision).toBe(6n) expect(c.base).toBe(2_440_000n) expect(c.toDecimalString()).toBe('2.440000') }) it('must add (p1 = p2) force right', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.right) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 > p2) force right', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.add(b, Decimals.right) expect(c.precision).toBe(6n) expect(c.base).toBe(2_440_000n) expect(c.toDecimalString()).toBe('2.440000') }) it('must add (p1 < p2) force right', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.right) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 = p2) force min', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.min) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 > p2) force min', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.add(b, Decimals.min) expect(c.precision).toBe(6n) expect(c.base).toBe(2_440_000n) expect(c.toDecimalString()).toBe('2.440000') }) it('must add (p1 < p2) force min', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.min) expect(c.precision).toBe(6n) expect(c.base).toBe(2_440_000n) expect(c.toDecimalString()).toBe('2.440000') }) it('must add (p1 = p2) force max', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.max) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 > p2) force max', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.add(b, Decimals.max) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) it('must add (p1 < p2) force max', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) const c = a.add(b, Decimals.max) expect(c.precision).toBe(9n) expect(c.base).toBe(2_440_000_000n) expect(c.toDecimalString()).toBe('2.440000000') }) }) describe('-', () => { it('must sub (p1 = p2)', () => { const a = fpFromDecimal('1.22', 9) const b = fpFromDecimal('0.22', 9) const c = a.sub(b) expect(c.precision).toBe(9n) expect(c.base).toBe(1_000_000_000n) expect(c.toDecimalString()).toBe('1.000000000') }) it('must sub (p1 > p2)', () => { const a = fpFromDecimal('0.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.sub(b) expect(c.precision).toBe(9n) expect(c.base).toBe(-1_000_000_000n) expect(c.toDecimalString()).toBe('-1.000000000') }) it('must sub (p1 < p2)', () => { const a = fpFromDecimal('0.00001', 6) const b = fpFromDecimal('0.0000001', 9) const c = a.sub(b) expect(c.precision).toBe(6n) expect(c.base).toBe(9n) expect(c.toDecimalString()).toBe('0.000009') }) it('must sub (p1 = p2) force left', () => { const a = fpFromDecimal('-2', 9) const b = fpFromDecimal('-3.5', 9) const c = a.sub(b, Decimals.left) expect(c.precision).toBe(9n) expect(c.base).toBe(1500000000n) expect(c.toDecimalString()).toBe('1.500000000') }) it('must sub (p1 > p2) force left', () => { const a = fpFromDecimal('-4', 9) const b = fpFromDecimal('2.333', 6) const c = a.sub(b, Decimals.left) expect(c.precision).toBe(9n) expect(c.base).toBe(-6333000000n) expect(c.toDecimalString()).toBe('-6.333000000') }) it('must sub (p1 < p2) force left', () => { const a = fpFromDecimal('1.22', 6) const b = fpFromDecimal('1.22', 9) const c = a.sub(b, Decimals.left) expect(c.precision).toBe(6n) expect(c.base).toBe(0n) expect(c.toDecimalString()).toBe('0.000000') }) it('must sub (p1 = p2) force right', () => { const a = fpFromDecimal('0.000000001', 9) const b = fpFromDecimal('0.000000002', 9) const c = a.sub(b, Decimals.right) expect(c.precision).toBe(9n) expect(c.base).toBe(-1n) expect(c.toDecimalString()).toBe('-0.000000001') }) it('must sub (p1 > p2) force right', () => { const a = fpFromDecimal('0.000000001', 9) const b = fpFromDecimal('0.000001', 6) const c = a.sub(b, Decimals.right) expect(c.precision).toBe(6n) expect(c.base).toBe(0n) expect(c.toDecimalString()).toBe('0.000000') }) it('must sub (p1 < p2) force right', () => { const a = fpFromDecimal('-3.000002', 6) const b = fpFromDecimal('-3.000001', 9) const c = a.sub(b, Decimals.right) expect(c.precision).toBe(9n) expect(c.base).toBe(-1000n) expect(c.toDecimalString()).toBe('-0.000001000') }) it('must sub (p1 = p2) force min', () => { const a = fpFromDecimal('1000', 9) const b = fpFromDecimal('0.000000999', 9) const c = a.sub(b, Decimals.min) expect(c.precision).toBe(9n) expect(c.base).toBe(999999999001n) expect(c.toDecimalString()).toBe('999.999999001') }) it('must sub (p1 > p2) force min', () => { const a = fpFromDecimal('5.12', 9) const b = fpFromDecimal('1.22', 6) const c = a.sub(b, Decimals.min) expect(c.precision).toBe(6n) expect(c.base).toBe(3900000n) expect(c.toDecimalString()).toBe('3.900000') }) it('must sub (p1 < p2) force min', () => { const a = fpFromDecimal('1.111111', 6) const b = fpFromDecimal('1.222222222', 9) const c = a.sub(b, Decimals.min) expect(c.precision).toBe(6n) expect(c.base).toBe(-111111n) expect(c.toDecimalString()).toBe('-0.111111') }) it('must sub (p1 = p2) force max', () => { const a = fpFromDecimal('1.111111', 9) const b = fpFromDecimal('1.222222222', 9) const c = a.sub(b, Decimals.max) expect(c.precision).toBe(9n) expect(c.base).toBe(-111111222n) expect(c.toDecimalString()).toBe('-0.111111222') }) it('must sub (p1 > p2) force max', () => { const a = fpFromDecimal('122.22', 9) const b = fpFromDecimal('1.22', 6) const c = a.sub(b, Decimals.max) expect(c.precision).toBe(9n) expect(c.base).toBe(121000000000n) expect(c.toDecimalString()).toBe('121.000000000') }) it('must sub (p1 < p2) force max', () => { const a = fpFromDecimal('122000', 6) const b = fpFromDecimal('333333.333333333', 9) const c = a.sub(b, Decimals.max) expect(c.precision).toBe(9n) expect(c.base).toBe(-211333333333333n) expect(c.toDecimalString()).toBe('-211333.333333333') }) }) describe('*', () => { it('must mul (1 * 10) add precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('10', 9) const c = a.mul(b, Decimals.add) expect(c.precision).toBe(18n) expect(c.base).toBe(10000000000000000000n) expect(c.toDecimalString()).toBe('10.000000000000000000') }) it('must mul (1 * 10) keep precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('10', 9) const c = a.mul(b) expect(c.precision).toBe(9n) expect(c.base).toBe(10000000000n) expect(c.toDecimalString()).toBe('10.000000000') }) it('must mul (0.1 * 0.2) add precision', () => { const a = fpFromDecimal('0.1', 9) const b = fpFromDecimal('0.2', 9) const c = a.mul(b, Decimals.add) expect(c.precision).toBe(18n) expect(c.base).toBe(20000000000000000n) expect(c.toDecimalString()).toBe('0.020000000000000000') }) it('must mul (0.1 * 0.2) keep precision', () => { const a = fpFromDecimal('0.1', 9) const b = fpFromDecimal('0.2', 9) const c = a.mul(b, Decimals.left) expect(c.precision).toBe(9n) expect(c.base).toBe(20000000n) expect(c.toDecimalString()).toBe('0.020000000') }) it('must mul (1e-9 * 2e-9) add precision', () => { const a = fpFromDecimal('0.000000001', 9) const b = fpFromDecimal('0.000000002', 9) const c = a.mul(b, Decimals.add) expect(c.precision).toBe(18n) expect(c.base).toBe(2n) expect(c.toDecimalString()).toBe('0.000000000000000002') }) it('must mul (0.1 * 0.2) keep precision', () => { const a = fpFromDecimal('0.000000001', 9) const b = fpFromDecimal('0.000000002', 9) const c = a.mul(b, Decimals.left) expect(c.precision).toBe(9n) expect(c.base).toBe(0n) expect(c.toDecimalString()).toBe('0.000000000') }) it('must mul (1 * 2 * 3 * 4) add precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('2', 9) const c = fpFromDecimal('3', 9) const d = fpFromDecimal('4', 9) const e = a.mul(b, Decimals.add).mul(c, Decimals.add).mul(d, Decimals.add) expect(e.precision).toBe(36n) expect(e.base).toBe(24000000000000000000000000000000000000n) expect(e.toDecimalString()).toBe('24.000000000000000000000000000000000000') }) it('must mul (1.33e9 * 2.33e9 * 3.33e9 * 4.33e9) max precision', () => { const a = fpFromDecimal('1330000000', 9) const b = fpFromDecimal('2330000000', 9) const c = fpFromDecimal('3330000000', 9) const d = fpFromDecimal('4330000000', 9) const e = a.mul(b, Decimals.add).mul(c, Decimals.add).mul(d, Decimals.add) expect(e.precision).toBe(36n) expect(e.base).toBe(44682729210000000000000000000000000000000000000000000000000000000000000000n) expect(e.toDecimalString()).toBe('44682729210000000000000000000000000000.000000000000000000000000000000000000') }) }) describe('/', () => { it('must div (1 / 10) keep precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('10', 9) const c = a.div(b) expect(c.precision).toBe(9n) expect(c.base).toBe(100000000n) expect(c.toDecimalString()).toBe('0.100000000') }) it('must div (1 / 1e9) keep precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('1000000000', 9) const c = a.div(b) expect(c.precision).toBe(9n) expect(c.base).toBe(1n) expect(c.toDecimalString()).toBe('0.000000001') }) it('must div (1 / 1e12) keep precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('1000000000000', 9) const c = a.div(b) expect(c.precision).toBe(9n) expect(c.base).toBe(0n) expect(c.toDecimalString()).toBe('0.000000000') }) it('must div (1 / 3) keep precision', () => { const a = fpFromDecimal('1', 9) const b = fpFromDecimal('3', 9) const c = a.div(b) expect(c.precision).toBe(9n) expect(c.base).toBe(333333333n) expect(c.toDecimalString()).toBe('0.333333333') }) }) }) describe('sqrt', () => { it('must calculate square root of perfect squares', () => { // sqrt(25) = 5 const d = fpFromDecimal('25', 6) const resultD = d.sqrt() expect(resultD.toDecimalString()).toBe('5.000000') }) it('must calculate square root of non-perfect squares', () => { // sqrt(2) ≈ 1.414213 const a = fpFromDecimal('2', 6) const result = a.sqrt() expect(result.toDecimalString()).toBe('1.414213') // sqrt(3) ≈ 1.732050 const b = fpFromDecimal('3', 6) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('1.732050') // sqrt(5) ≈ 2.236067 const c = fpFromDecimal('5', 6) const resultC = c.sqrt() expect(resultC.toDecimalString()).toBe('2.236067') // sqrt(10) ≈ 3.162277 const d = fpFromDecimal('10', 6) const resultD = d.sqrt() expect(resultD.toDecimalString()).toBe('3.162277') }) it('must calculate square root of decimal numbers', () => { // sqrt(0.25) = 0.5 const a = fpFromDecimal('0.25', 6) const result = a.sqrt() expect(result.toDecimalString()).toBe('0.500000') // sqrt(0.5) ≈ 0.707106 const b = fpFromDecimal('0.5', 6) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('0.707106') // sqrt(1.44) = 1.2 const c = fpFromDecimal('1.44', 6) const resultC = c.sqrt() expect(resultC.toDecimalString()).toBe('1.200000') // sqrt(2.25) = 1.5 const d = fpFromDecimal('2.25', 6) const resultD = d.sqrt() expect(resultD.toDecimalString()).toBe('1.500000') }) it('must calculate square root of very small numbers', () => { // sqrt(0.000001) = 0.001 const a = fpFromDecimal('0.000001', 6) const result = a.sqrt() expect(result.toDecimalString()).toBe('0.001000') // sqrt(0.000004) = 0.002 const b = fpFromDecimal('0.000004', 6) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('0.002000') // sqrt(0.000009) = 0.003 const c = fpFromDecimal('0.000009', 6) const resultC = c.sqrt() expect(resultC.toDecimalString()).toBe('0.003000') }) it('must calculate square root of very large numbers', () => { // sqrt(100) = 10 const a = fpFromDecimal('100', 6) const result = a.sqrt() expect(result.toDecimalString()).toBe('10.000000') // sqrt(10000) = 100 const b = fpFromDecimal('10000', 6) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('100.000000') // sqrt(1000000) = 1000 const c = fpFromDecimal('1000000', 6) const resultC = c.sqrt() expect(resultC.toDecimalString()).toBe('1000.000000') }) it('must calculate square root of 1', () => { // sqrt(1) = 1 const a = fpFromDecimal('1', 6) const result = a.sqrt() expect(result.toDecimalString()).toBe('1.000000') // Test with different precision const b = fpFromDecimal('1', 9) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('1.000000000') }) it('must calculate square root of 0', () => { // sqrt(0) = 0 const a = fpFromDecimal('0', 6) const result = a.sqrt() expect(result.toDecimalString()).toBe('0.000000') // Test with different precision const b = fpFromDecimal('0', 9) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('0.000000000') }) it('must throw error for negative numbers', () => { const a = fpFromDecimal('-1', 6) expect(() => a.sqrt()).toThrow('Cannot calculate square root of negative number') const b = fpFromDecimal('-0.5', 6) expect(() => b.sqrt()).toThrow('Cannot calculate square root of negative number') const c = fpFromDecimal('-100', 6) expect(() => c.sqrt()).toThrow('Cannot calculate square root of negative number') }) it('must work with different precisions', () => { // Test with precision 2 const a = fpFromDecimal('2', 2) const result = a.sqrt() expect(result.toDecimalString()).toBe('1.41') // Test with precision 3 const b = fpFromDecimal('2', 3) const resultB = b.sqrt() expect(resultB.toDecimalString()).toBe('1.414') // Test with precision 9 const c = fpFromDecimal('2', 9) const resultC = c.sqrt() expect(resultC.toDecimalString()).toBe('1.414213562') }) it('must have squareRoot alias', () => { const a = fpFromDecimal('4', 6) const result1 = a.sqrt() const result2 = a.squareRoot() expect(result1.toDecimalString()).toBe(result2.toDecimalString()) expect(result1.toDecimalString()).toBe('2.000000') }) it('must preserve precision of original number', () => { const a = fpFromDecimal('9', 6) const result = a.sqrt() expect(result.precision).toBe(6n) const b = fpFromDecimal('9', 9) const resultB = b.sqrt() expect(resultB.precision).toBe(9n) }) it('must handle edge cases with high precision', () => { // Test a number that requires multiple Newton-Raphson iterations const a = fpFromDecimal('123.456789', 9) const result = a.sqrt() // sqrt(123.456789) ≈ 11.111111060 expect(result.toDecimalString()).toBe('11.111111060') }) }) })