Skip to content

is_zero() for floats does an exact comparison #23

@Mitchelldscott

Description

@Mitchelldscott

Should probably replace the is_zero() calls with something that supports relative eq/rounding errors. There is a comparison function for f32 and f64 in math::assert_f, should pull the logic out of the macro and use it in a custom Zero/One trait impl.

ChatGPT Explaination:

Great question — here's the core reason:


❗ You should not use exact comparisons (like ==, or is_zero()) for f32 and f64 in most numerical code because floating-point arithmetic is inherently imprecise due to:

1. Rounding Errors

  • Operations like addition, subtraction, multiplication, and division often result in small rounding differences that accumulate.

  • For example:

    let x = 0.1 + 0.2;
    println!("{}", x == 0.3); // false!
    

2. Representation Limits

  • Many decimal numbers cannot be exactly represented in binary floating-point. For example, 0.1 in f64 is actually:

    0.10000000000000000555...
    

3. is_zero() is an exact comparison

  • num_traits::Float::is_zero() is implemented as self == T::zero(), which is an exact bitwise comparison — and that’s rarely what you want when checking results from computation.


✅ What to Use Instead: Epsilon-based Approximate Comparison

This checks whether a number is close enough to zero (or another value), within a tolerance:

(x - y).abs() < EPSILON

Or use approx crate macros:

use approx::assert_relative_eq;

assert_relative_eq!(computed, expected, epsilon = 1e-12);

Or define a helper for near-zero:

fn is_near_zero(x: f64, tol: f64) -> bool {
    x.abs() < tol
}

✅ When Exact Comparison Is Okay:

  • You're comparing constants, not results of arithmetic:

    let a = 1.0f64;
    assert_eq!(a, 1.0); // fine
    
  • You're working with types like u32, i32, or bool.


Summary:

Comparison Type Good Practice? Use Instead
f64 == 0.0 ❌ No x.abs() < 1e-12 or approx crate
is_zero() on floats ❌ No Custom is_near_zero(x, tol)
Comparing constants ✅ Yes

Let me know if you want a utility function or trait to handle approximate comparisons safely in your library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't workingCode QualityRelates to the overall quality of the code-base

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions