How to compare floating-point values safely in Swift

3 Answers

0 votes
/*
====================================================================
 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

*/

 



answered 6 days ago by avibootz
0 votes
/*
====================================================================
 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

func approximatelyEqual(
    _ a: Double,
    _ b: Double,
    epsilon: Double = 1e-9
) -> Bool {
    return abs(a - b) <= epsilon
}

/*
 * 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

*/

 



answered 6 days ago by avibootz
0 votes
import Foundation

// For financial calculations, prefer Decimal instead of Double

/*
 * Main Program
 */

let a = Decimal(string: "0.1")!
let b = Decimal(string: "0.2")!

let result = a + b

if result == Decimal(string: "0.3")! {
    print("Exactly equal")
}



/*
run:

Exactly equal

*/

 



answered 6 days ago by avibootz
...