# Is it safe to compare rounded float values for equality?

**URL:** https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278
**Category:** General Usage
**Tags:** faq
**Created:** [February 27, 2019, 8:36pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278 "2019-02-27T20:36:13Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)
#### Post date: [February 27, 2019, 8:36pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/1 "2019-02-27T20:36:13Z")

</div>

I know that comparing two floats for equality using `==` is not a good idea, as it can yield unexpected results. For example, `0.1 + 0.2 == 0.3` returns `false`. I understand that there is an `isapprox` function to deal with this. My question is the following. Is it safe to compare two float values if they have been rounded to the same number of digits?

For example, `round(0.1+0.2, digits=2) == round(0.3, digits=2)` returns `true`. Can I expect this to always work as long as the two floating point numbers are rounded to the same two digits?

Thank you.

---

<div class="post-metadata">

### Author: ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)
#### Post date: [February 27, 2019, 9:18pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/2 "2019-02-27T21:18:52Z")

</div>

You can just do this

```julia
julia> epsilon = 1.0e-10
1.0e-10

julia> if abs( (0.1 + 0.2) - 0.3 ) <= epsilon
       println("A and B are equsl")
       else
       println("A and B are NOT equal")
       end
A and B are equsl

```

You can create your own function

```julia
julia> function IsEqual(A::Float64,B::Float64,epsilon::Float64=1.0e-10)
           if abs( A - B ) <= epsilon
               return true
           else
               return false
           end
       end

julia> IsEqual(0.1 + 0.2 , 0.3)
true

julia> println("Just make sure epsilon is bigger than ", abs( (0.1 + 0.2) - 0.3 ) )
Just make sure epsilon is bigger than 5.551115123125783e-17

```

---

<div class="post-metadata">

### Author: ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)
#### Post date: [February 27, 2019, 9:22pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/3 "2019-02-27T21:22:29Z")

</div>

Yes, I believe that should be safe. I’m curious why you’d want to do this though, and not use:

```julia
julia> 0.1 + 0.2 ≈ 0.3
true

```

Or, for more control over the tolerance used, use [isapprox](https://docs.julialang.org/en/latest/base/math/#Base.isapprox). For example:

```julia
julia> isapprox(0.1 + 0.2, 0.3; atol = 1e-2)
true

```

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [February 27, 2019, 9:40pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/4 "2019-02-27T21:40:25Z")

</div>

> [@StevenSiew](#):
>
> ```julia-auto
> julia> function IsEqual(A::Float64,B::Float64,epsilon::Float64=1.0e-10)
> if abs( A - B ) <= epsilon
> return true
> else
> return false
> end
> end
> 
> ```

Ooofff. I suppose you mean:

```julia-auto
function IsEqual(A::Float64,B::Float64,epsilon::Float64=1.0e-10)
    return abs( A - B ) <= epsilon
end

```

😉

---

<div class="post-metadata">

### Author: ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)
#### Post date: [February 27, 2019, 9:49pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/5 "2019-02-27T21:49:16Z")

</div>

Actually, it’s not safe…

```julia
julia> round(1e30 + 2e30; digits=2) == round(3e30; digits=2)
false

```

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 27, 2019, 9:57pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/6 "2019-02-27T21:57:04Z")

</div>

> [@curious](#):
>
> Is it safe to compare two float values if they have been rounded to the same number of digits?

I’m guessing that what you mean by “safe” is _Would it give the same answer as it would in exact (infinite-precision) arithmetic?_ The answer, of course, is “it depends”, but the most general answer is “no”.

That is, suppose you are comparing two numbers `x` and `y` that are computed by two different floating-point algorithms, and you want a comparison function `is_same(x,y)` that returns `true` if you would have `x==y` in infinite precision.

Suppose that you your algorithms are accurate to 8 significant digits. Then you could do `isapprox(x, y, rtol=1e-8)`. Or you could do `round(x, sigdigits=8) == round(y, sigdigits=8)`, which is almost equivalent but much slower (about 100× slower on my computer!).

Of course, to do this, you need to have a rough sense of the accuracy of your algorithms. If it is a single scalar operation like `0.1 + 0.2`, then it should be accurate to nearly machine precision, but for more complicated algorithms error analysis is much tricker. The default in `isapprox` (the `≈` operator) is to compare about half of the significant digits in the current precision, which is reasonable for many algorithms (losing more than half of the significant digits means you have a pretty inaccurate calculation), but is obviously not universally appropriate.

Naturally, be aware that such approximate comparisons may give false positives (returning `true` for two values that are _supposed_ to be distinct in infinite precision, but differ by a very small amount).

Your suggestion, `round(x, digits=8) == round(y, digits=8)`, is roughly equivalent to (but vastly slower than) `isapprox(x, y, atol=1e-8)` — an _absolute_ tolerance rather than a relative tolerance. Usually, a relative tolerance is more appropriate in floating-point calculations, because relative tolerances are scale invariant.

If you want a rigorous guarantee that two values _might_ be the same, you can use [Interval Arithmetic](https://github.com/JuliaIntervals/IntervalArithmetic.jl) and implement `might_be_same(x,y) = !isdisjoint(x,y)`. This might give you false positives, but will never give false negatives.

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 27, 2019, 10:11pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/7 "2019-02-27T22:11:20Z")

</div>

> [@StevenSiew](#):
>
> You can just do this …

The function you wrote is equivalent to calling `isapprox` with the `atol` keyword. As I mentioned above, however, typically a relative tolerance (`rtol`) is more appropriate.

---

<div class="post-metadata">

### Author: ![simonbyrne](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simonbyrne/32/19_2.png) [@simonbyrne](https://discourse.julialang.org/u/simonbyrne)
#### Post date: [February 27, 2019, 11:52pm UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/8 "2019-02-27T23:52:35Z")

</div>

> [@curious](#):
>
> For example, `round(0.1+0.2, digits=2) == round(0.3, digits=2)` returns `true` . Can I expect this to always work as long as the two floating point numbers are rounded to the same two digits?

No (unfortunately I speak from experience where I worked on a project that did this). The problem is that two nearby values can round in opposite directions, e.g.

```julia
julia> round(0.014999999999999, digits=2)
0.01

julia> round(0.015000000000000, digits=2)
0.02

```

Now that might seem unlikely, but if you test enough values, it will probably happen. e.g. if the values are accurate up to `1e-8`, and you check to 2 decimal places, the probability of it happening for any one test is 1 in million:

```julia
julia> 1e-8/1e-2
1.0e-6

```

But if you’re testing say a 500\*500 matrix then you would see it happen ~22% of the time:

```julia
julia> 1-(1-(1e-8/1e-2))^(500*500)
0.22119931428435058

```

---

<div class="post-metadata">

### Author: ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)
#### Post date: [February 28, 2019, 12:35am UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/9 "2019-02-28T00:35:06Z")

</div>

Thanks very much for all the replies. I need to read some more carefully to digest all the information. The reason I was trying to avoid using `isapprox` is that I want to use these values as keys in a dictionary, and was wondering if I could use the  
`get(d, k, 0)` to get the value for key `k` (where `k` is a float). I was thinking of using rounded floats as keys. Using `isapprox` would require me to explicitly loop through the keys to test whether the key exists. But it seems that is the safer way. Essentially I am trying to get by with Floats when I should be using decimals (via Decimals package).

Thanks again.

---

<div class="post-metadata">

### Author: ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)
#### Post date: [February 28, 2019, 12:50am UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/10 "2019-02-28T00:50:40Z")

</div>

Any bucketing of floating-point numbers into more than one bucket will have the property that there are values which only differ in the last bit yet are in different buckets.

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [February 28, 2019, 1:15am UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/11 "2019-02-28T01:15:26Z")

</div>

For quick and dirty code, you can use

```julia
julia> _round_tozero(r::Float64, sigdigs) = reinterpret(Float64, reinterpret(UInt64,r) & (-1<< (63&(52-sigdigs))))
julia> _round_fromzero(r::Float64, sigdigs) = reinterpret(Float64, reinterpret(UInt64,r) | ~(-1<< (63&(52-sigdigs))))

```

I am sure there is a way of expressing this using julia’s built in rounding modes. The above is definitely close to the fastest possible code, with unexpected gotchas like `isnan(_round_fromzero(Inf, 3))` or `_round_tozero(NaN,0)==Inf` (both are arguably wrong).

---

<div class="post-metadata">

### Author: ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)
#### Post date: [February 28, 2019, 6:32am UTC](https://discourse.julialang.org/t/is-it-safe-to-compare-rounded-float-values-for-equality/21278/12 "2019-02-28T06:32:40Z")

</div>

> [@curious](#):
>
> Can I expect this to always work as long as the two floating point numbers are rounded to the same two digits?

To be specific, I would say yes to this question. The problem occurs when the two numbers are _not_ rounded to the same two digits.

> [@curious](#):
>
> The reason I was trying to avoid using `isapprox` is that I want to use these values as keys in a dictionary

Thanks for clarifying, that helps. As you say, one option is to use `Decimals` for this. Another would be to use a sorted dictionary, and find the closest mapping, and see if that’s within your accepted tolerance. If your dictionary has many elements, this should be faster than looping over them all, since it has logarithmic instead of linear complexity in the number of elements. For example, here’s your current problem using `SortedDict`:

```julia
julia> using DataStructures

julia> d = SortedDict{Float64, String}();

julia> d[0.1 + 0.2] = "foo";

julia> d[0.3]
ERROR: KeyError: key 0.3 not found

```

Now define:

```julia
function closestmapping(d::SortedDict{K,V}, k::K) where {K<:Real, V}
    t1 = searchsortedlast(d,k)
    t2 = advance((d,t1))
    m = map(t -> deref((d,t)), Iterators.filter(t -> status((d,t)) == 1, (t1,t2)))
    reduce((a,b) -> abs(k - a[1]) < abs(k - b[1]) ? a : b, m)
end

```

Then you could do:

```julia
julia> c = closestmapping(d, 0.3)
0.3 => "foo"

julia> c[1] - 0.3
5.551115123125783e-17

```

(Of course, if your algorithm is only accurate to say 8 digits, and you have multiple keys within 1e-8 of each other, this approach won’t work.)
