feat(utils): add a util for float approx comparison

This commit is contained in:
Jiri Zbytovsky
2025-06-01 17:08:58 +02:00
committed by Jiri Zbytovsky
parent 7ae05a7602
commit 24f4d182bc
3 changed files with 57 additions and 0 deletions

View File

@@ -32,6 +32,7 @@ export * from './getWeakRandomNumberInRange';
export * from './hasUppercaseLetter';
export { hexToRgba } from './hexToRgba';
export { hexToRgbaArray } from './hexToRgbaArray';
export { isApproximatelyEqual } from './isApproximatelyEqual';
export * from './isArrayMember';
export * from './isFullPath';
export * from './isHex';

View File

@@ -0,0 +1,24 @@
import { BigNumber, BigNumberValue } from './bigNumber';
/**
* Check if two BigNumber values are approximately equal based on relative difference
* @param value1
* @param value2
* @param relativeTolerance maximum threshold for relative difference of values to declare them approx. equal
*/
export const isApproximatelyEqual = (
value1: BigNumberValue,
value2: BigNumberValue,
relativeTolerance: BigNumberValue,
): boolean => {
value1 = new BigNumber(value1);
value2 = new BigNumber(value2);
relativeTolerance = new BigNumber(relativeTolerance);
// Cannot calculate relative difference if one value is zero, but then they must be equal
if (value1.eq(0)) return value1.eq(value2);
const relativeDifference = value2.minus(value1).abs().dividedBy(value1);
return relativeDifference.lte(relativeTolerance);
};

View File

@@ -0,0 +1,32 @@
import { BigNumber } from '../src/bigNumber';
import { isApproximatelyEqual } from '../src/isApproximatelyEqual';
describe(isApproximatelyEqual.name, () => {
it('always returns true for equal values', () => {
expect(isApproximatelyEqual(100, 100, 0.001)).toBe(true);
expect(isApproximatelyEqual(100, '100', 0.001)).toBe(true);
expect(isApproximatelyEqual('100', '100', 0.001)).toBe(true);
expect(isApproximatelyEqual(new BigNumber(-123), '-123', 0.001)).toBe(true);
expect(isApproximatelyEqual('-123.001', '-123.001000', 0.001)).toBe(true);
});
it('handles zero values correctly', () => {
expect(isApproximatelyEqual(0, 0, 0.01)).toBe(true);
expect(isApproximatelyEqual(0, 1, 0.01)).toBe(false);
});
it('handles relative tolerance', () => {
expect(isApproximatelyEqual(100, 101, 0.005)).toBe(false);
expect(isApproximatelyEqual(100, 101, 0.01)).toBe(true);
expect(isApproximatelyEqual(100, 101, 0.02)).toBe(true);
});
it('handles NaN values', () => {
expect(isApproximatelyEqual(100, NaN, 0.01)).toBe(false);
expect(isApproximatelyEqual(NaN, 100, 0.01)).toBe(false);
expect(isApproximatelyEqual(NaN, NaN, 0.01)).toBe(false);
expect(isApproximatelyEqual('nonsense', '123', 0.01)).toBe(false);
expect(isApproximatelyEqual('123', 'nonsense', 0.01)).toBe(false);
expect(isApproximatelyEqual('nonsense', '', 0.01)).toBe(false);
});
});