/*
====================================================================
Title: Safe Floating-Point Comparison in Swift
Purpose:
Demonstrates how to compare floating-point values safely
using absolute and relative tolerances.
Why not use == ?
Floating-point values (Float and Double) are stored as
binary approximations. Many decimal values cannot be
represented exactly, which can introduce small rounding
errors during calculations.
Example:
0.1 + 0.2 != 0.3
Although mathematically equal, the actual binary
representations may differ slightly.
====================================================================
*/
import Foundation
/*
* Recommended tolerance values.
*
* Adjust these values based on the precision requirements
* of your application.
*/
let absoluteTolerance: Double = 1e-12
let relativeTolerance: Double = 1e-9
/*
* Function: approximatelyEqual
*
* Safely compares two floating-point values using:
* 1. Absolute tolerance
* 2. Relative tolerance
*
* Why both?
* - Absolute tolerance works well for values near zero.
* - Relative tolerance works well for larger values.
*
* Parameters:
* a - First value
* b - Second value
*
* Returns:
* true -> Values are considered equal
* false -> Values are different
*/
func approximatelyEqual(
_ a: Double,
_ b: Double
) -> Bool {
// Calculate the absolute difference.
let difference: Double = abs(a - b)
/*
* Compare against the larger of:
* 1. Absolute tolerance
* 2. Relative tolerance scaled by magnitude
*
* This makes the comparison reliable across
* different ranges of values.
*/
return difference <= max(
absoluteTolerance,
max(abs(a), abs(b)) * relativeTolerance
)
}
/*
* Main Program
*/
// Classic floating-point precision example.
let x: Double = 0.1 + 0.2
let y: Double = 0.3
print("================================================")
print(" Safe Floating-Point Comparison in Swift")
print("================================================")
print()
/*
* Display many decimal places so the small
* precision difference becomes visible.
*/
print(String(format: "x = %.17f", x))
print(String(format: "y = %.17f", y))
print()
/*
* BAD PRACTICE:
* Direct comparison using ==.
*
* Small rounding errors may cause values that should
* be considered equal to compare as unequal.
*/
if x == y {
print("Direct comparison (==): Equal")
} else {
print("Direct comparison (==): Not Equal")
}
print()
/*
* GOOD PRACTICE:
* Compare using approximatelyEqual().
*/
if approximatelyEqual(x, y) {
print("approximatelyEqual(): Equal")
} else {
print("approximatelyEqual(): Not Equal")
}
print()
/*
* Additional test cases.
*/
let largeA: Double = 1_000_000.000000001
let largeB: Double = 1_000_000.000000002
let nearZeroA: Double = 1e-12
let nearZeroB: Double = 0.0
print("Additional Tests")
print("----------------")
print(
"Large values equal: \(approximatelyEqual(largeA, largeB))"
)
print(
"Near-zero values equal: \(approximatelyEqual(nearZeroA, nearZeroB))"
)
print(
"1.5 and 2.0 equal: \(approximatelyEqual(1.5, 2.0))"
)
/*
run:
================================================
Safe Floating-Point Comparison in Swift
================================================
x = 0.30000000000000004
y = 0.29999999999999999
Direct comparison (==): Not Equal
approximatelyEqual(): Equal
Additional Tests
----------------
Large values equal: true
Near-zero values equal: true
1.5 and 2.0 equal: false
*/