/*
====================================================================
Title: Safe Floating-Point Comparison in TypeScript
Purpose:
Demonstrates how to compare floating-point values safely
using absolute and relative tolerances instead of ===.
Why not use === ?
JavaScript/TypeScript numbers are represented using the
IEEE 754 double-precision floating-point format.
Many decimal values cannot be represented exactly in binary.
Example:
0.1 + 0.2 !== 0.3
Tiny rounding errors may cause values that should be
mathematically equal to differ slightly in memory.
====================================================================
*/
/*
* Function: approximatelyEqual
*
* Safely compares two floating-point values.
*
* Parameters:
* a - First value
* b - Second value
* absTolerance - Tolerance for values near zero
* relTolerance - Tolerance scaled by magnitude
*
* Returns:
* true -> Values are considered equal
* false -> Values are different
*
* Best Practice:
* Use both absolute and relative tolerances.
* This works well for small and large numbers.
*/
function approximatelyEqual(
a: number,
b: number,
absTolerance: number = 1e-12,
relTolerance: number = 1e-9
): boolean {
// Calculate the absolute difference.
const diff: number = Math.abs(a - b);
/*
* Compare against the larger of:
* 1. The absolute tolerance
* 2. The relative tolerance adjusted for scale
*/
return diff <= Math.max(
absTolerance,
Math.max(Math.abs(a), Math.abs(b)) * relTolerance
);
}
/*
* Main Program
*/
// Example values that expose floating-point precision issues.
const x: number = 0.1 + 0.2;
const y: number = 0.3;
console.log("================================================");
console.log(" Safe Floating-Point Comparison in TypeScript");
console.log("================================================");
console.log("");
// Display high precision to reveal rounding differences.
console.log(`x = ${x.toPrecision(17)}`);
console.log(`y = ${y.toPrecision(17)}`);
console.log("");
/*
* BAD PRACTICE:
* Direct comparison using ===.
*
* Floating-point rounding errors may cause this
* comparison to fail unexpectedly.
*/
if (x === y) {
console.log("Direct comparison (===): Equal");
} else {
console.log("Direct comparison (===): Not Equal");
}
console.log("");
/*
* GOOD PRACTICE:
* Compare using approximatelyEqual().
*/
if (approximatelyEqual(x, y)) {
console.log("approximatelyEqual(): Equal");
} else {
console.log("approximatelyEqual(): Not Equal");
}
console.log("");
/*
* Additional test cases
*/
const largeA: number = 1000000.000000001;
const largeB: number = 1000000.000000002;
const nearZeroA: number = 0.000000000001;
const nearZeroB: number = 0;
console.log("Additional Tests:");
console.log("-----------------");
console.log(
`Large numbers equal: ${approximatelyEqual(
largeA,
largeB
)}`
);
console.log(
`Near zero equal: ${approximatelyEqual(
nearZeroA,
nearZeroB
)}`
);
console.log(
`1.5 vs 2.0 equal: ${approximatelyEqual(
1.5,
2.0
)}`
);
/*
run:
================================================
Safe Floating-Point Comparison in TypeScript
================================================
x = 0.30000000000000004
y = 0.29999999999999999
Direct comparison (===): Not Equal
approximatelyEqual(): Equal
Additional Tests:
-----------------
Large numbers equal: true
Near zero equal: true
1.5 vs 2.0 equal: false
*/