Float numbers equality.

classic Classic list List threaded Threaded
44 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Float numbers equality.

Karol Dro
Hi,

I'm writing some unit tests, where I compare numbers and I have problem with something like this: 
300 * 0.07 == 21 => false.
print (300 * 0.07, 21, 300 * 0.07 == 21)

What is the best way, for test float equality?

I consider to use tostring function:
print (300 * 0.07, 21, tostring (300 * 0.07) == tostring (21)),
But i wonder, maybe there is a better way, to compare something like this?

Thanks for any response.

--
Karol Drożak
Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Thomas Jericke
Hi

On 08/09/2013 12:53 PM, Karol Dro wrote:
>
> What is the best way, for test float equality?
>
You don't.

If you really need to, you need to compare with epsilon:

epsilon = X * 0.00001
XequalY = ((X - epsilon < Y) and (X + espilon > Y))

--
Thomas

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Luiz Henrique de Figueiredo
In reply to this post by Karol Dro
> What is the best way, for test float equality?
>
> I consider to use tostring function:
> print (300 * 0.07, 21, tostring (300 * 0.07) == tostring (21)),

For this kind of numbers I think this is the best option, especially
since it's in a unit test. But you make sure you know why tests like
300 * 0.07 == 21 give false. Otherwise, tell us more about the problem
you're trying to solve.

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Andrew Starks
In reply to this post by Karol Dro


On Friday, August 9, 2013, Karol Dro wrote:
Hi,

I'm writing some unit tests, where I compare numbers and I have problem with something like this: 
300 * 0.07 == 21 => false.
print (300 * 0.07, 21, 300 * 0.07 == 21)

What is the best way, for test float equality?

I consider to use tostring function:
print (300 * 0.07, 21, tostring (300 * 0.07) == tostring (21)),
But i wonder, maybe there is a better way, to compare something like this?

Thanks for any response.

--
Karol Drożak

This is exactly the problem that I've been working with. My first attempt was to do it is to do the calculation in the same way as I did in the function, but that created a tight coupling. 

The format("%.10f", x) method was the most reasonable, for my testing needs, because it allowed me to specify the precision that I needed.

In the end, floats were too challenging to work with because my conversions were being rounded down or up, depending upon where the conversion was coming from and the size of the values.

I decided to go with integers by using Luiz's lint64 package. I keep multiplying everything as long as possible and then deal with division at the last step. I mod the division if I need to know what the partial portion of the sample is. 

I don't know your specific case and others made my same suggestion. I just couldn't help but chime in, given this has been my life for the last few days. :)

-Andrew


Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Matthias Kluwe
In reply to this post by Karol Dro
Hi!

2013/8/9 Karol Dro <[hidden email]>:

> I'm writing some unit tests, where I compare numbers and I have problem with
> something like this:
> 300 * 0.07 == 21 => false.
> print (300 * 0.07, 21, 300 * 0.07 == 21)
>
> What is the best way, for test float equality?
>
> I consider to use tostring function:
> print (300 * 0.07, 21, tostring (300 * 0.07) == tostring (21)),
> But i wonder, maybe there is a better way, to compare something like this?

In short: Use a reasonable "epsilon".

But excuse me for mentioning the usual hint: Do a search for "What
Every Computer Scientist Should Know About Floating-Point Arithmetic".
The document may be a bit lengthy and answer a few more questions than
you have, but it tells the basic characteristics of floating point
arithmetics including important points as cancellation and the IEEE
standard.

Regards,
Matthias

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Coda Highland
On Fri, Aug 9, 2013 at 12:37 PM, Matthias Kluwe <[hidden email]> wrote:

> Hi!
>
> 2013/8/9 Karol Dro <[hidden email]>:
>
>> I'm writing some unit tests, where I compare numbers and I have problem with
>> something like this:
>> 300 * 0.07 == 21 => false.
>> print (300 * 0.07, 21, 300 * 0.07 == 21)
>>
>> What is the best way, for test float equality?
>>
>> I consider to use tostring function:
>> print (300 * 0.07, 21, tostring (300 * 0.07) == tostring (21)),
>> But i wonder, maybe there is a better way, to compare something like this?
>
> In short: Use a reasonable "epsilon".

Slightly longer "in short" -- this means use something like:

function float_equal(lhs, rhs, epsilon)
    return math.abs(lhs - rhs) < epsilon
end

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Luiz Henrique de Figueiredo
> function float_equal(lhs, rhs, epsilon)
>     return math.abs(lhs - rhs) < epsilon
> end

As mentioned before, if you have to do this, you need to use *relative* error,
not absolute error:
      return math.abs(lhs - rhs) < epsilon*rhs

Floating-poin numbers are not distributed uniformily in their range;
there's lot of clustering and empty intervals, due to scaling.

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Coda Highland
On Fri, Aug 9, 2013 at 3:18 PM, Luiz Henrique de Figueiredo
<[hidden email]> wrote:

>> function float_equal(lhs, rhs, epsilon)
>>     return math.abs(lhs - rhs) < epsilon
>> end
>
> As mentioned before, if you have to do this, you need to use *relative* error,
> not absolute error:
>       return math.abs(lhs - rhs) < epsilon*rhs
>
> Floating-poin numbers are not distributed uniformily in their range;
> there's lot of clustering and empty intervals, due to scaling.
>

Ah, whoops, yeah, you're right, I got sloppy there. Really that ought
to be epsilon*math.max(lhs,rhs) even.

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Coda Highland
On Fri, Aug 9, 2013 at 3:47 PM, Coda Highland <[hidden email]> wrote:

> On Fri, Aug 9, 2013 at 3:18 PM, Luiz Henrique de Figueiredo
> <[hidden email]> wrote:
>>> function float_equal(lhs, rhs, epsilon)
>>>     return math.abs(lhs - rhs) < epsilon
>>> end
>>
>> As mentioned before, if you have to do this, you need to use *relative* error,
>> not absolute error:
>>       return math.abs(lhs - rhs) < epsilon*rhs
>>
>> Floating-poin numbers are not distributed uniformily in their range;
>> there's lot of clustering and empty intervals, due to scaling.
>>
>
> Ah, whoops, yeah, you're right, I got sloppy there. Really that ought
> to be epsilon*math.max(lhs,rhs) even.
>
> /s/ Adam

Er. Actually, I just need to get some sleep. It doesn't matter which
side you multiply by there because if the two terms are close enough
that an epsilon comparison is necessary in the first place then you're
going to get basically the same result.

I don't usually need to do that kind of order-of-magnitude scaling
because my epsilon comparisons are with code where I already know the
range of the numbers involved.

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Enrico Colombini
In reply to this post by Coda Highland
On 10/08/2013 0.47, Coda Highland wrote:
> Ah, whoops, yeah, you're right, I got sloppy there. Really that ought
> to be epsilon*math.max(lhs,rhs) even.

Do you mean epsilon*math.max(math.abs(lhs),math.abs(rhs)) ?

--
   Enrico

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Egor Skriptunoff-2
In reply to this post by Luiz Henrique de Figueiredo

On Sat, Aug 10, 2013 at 1:18 AM, Luiz Henrique de Figueiredo <[hidden email]> wrote:
> function float_equal(lhs, rhs, epsilon)
>     return math.abs(lhs - rhs) < epsilon
> end

As mentioned before, if you have to do this, you need to use *relative* error,
not absolute error:
      return math.abs(lhs - rhs) < epsilon*rhs

Two zeros will never be equal to each other :-(
Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Dirk Laurie-2
In reply to this post by Karol Dro
> What is the best way, for test float equality?

Since everybody else is clearly expert enough not worry about
the following point, I suppose it does not need mentioning. But
still …

Once you have a satisfactory `float_eq` function, don't think that

debug.setmetatable(0.0,{__eq=float_eq})

will hencefore allow you to test `if a==b` by that metamethod.
Please do not think that.

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Coda Highland
In reply to this post by Enrico Colombini
On Sat, Aug 10, 2013 at 12:48 AM, Enrico Colombini <[hidden email]> wrote:

> On 10/08/2013 0.47, Coda Highland wrote:
>>
>> Ah, whoops, yeah, you're right, I got sloppy there. Really that ought
>> to be epsilon*math.max(lhs,rhs) even.
>
>
> Do you mean epsilon*math.max(math.abs(lhs),math.abs(rhs)) ?
>
> --
>   Enrico
>

No, but you have a good point that it should be math.abs(epsilon*rhs).

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Tim Hill
Given that this subject comes up every couple of months, and that there are some subtleties to it, that this warrants a new function in the math library to do it the right way?

On Aug 10, 2013, at 9:04 AM, Coda Highland <[hidden email]> wrote:

> On Sat, Aug 10, 2013 at 12:48 AM, Enrico Colombini <[hidden email]> wrote:
>> On 10/08/2013 0.47, Coda Highland wrote:
>>>
>>> Ah, whoops, yeah, you're right, I got sloppy there. Really that ought
>>> to be epsilon*math.max(lhs,rhs) even.
>>
>>
>> Do you mean epsilon*math.max(math.abs(lhs),math.abs(rhs)) ?
>>
>> --
>>  Enrico
>>
>
> No, but you have a good point that it should be math.abs(epsilon*rhs).
>
> /s/ Adam
>


Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Jay Carlson
On Aug 10, 2013, at 3:08 PM, Tim Hill wrote:

[context:
> for i=1,78 do s="." if (i*0.1)*10~=i then s="X" end io.write(s) end print()
..X..XX....X.X........XX...XX...........X....X.X..X.X..X.X..X.X...............
]

> Given that this subject comes up every couple of months, and that there are some subtleties to it, that this warrants a new function in the math library to do it the right way?

The criteria for inclusion in the standard Lua library aren't clear to me. The module system is there since it's a minimal thing everybody needs to coordinate on. Is that true of this?

Much of the library seems to be important functionality for everyday use which cannot be implemented outside C, at least efficiently. That's definitely not true of equals_within_epsilon, so that suggests the place it would go is Microlight. Maybe.

Some people, including me, wish that more of _PiL_ were in the manual. But I'm not volunteering to write the gloss.

Jay
Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Luiz Henrique de Figueiredo
In reply to this post by Tim Hill
> Given that this subject comes up every couple of months, and that there are some subtleties to it, that this warrants a new function in the math library to do it the right way?

I don't think so. Floating-point arithmetic is not simple. I think it'd be
a disservice to try to hide its complexities in a official function that
may give the impression that it solves the problem reliably, when it can't.

On the other hand, there is nothing instrinsically wrong with floating-point
equality. The only catches come from numbers that should be equal but are
computed in two different ways and from naive expectations such as
300 * 0.7 == 21, due to a misunderstanding that floating-point representation
is a binary one, not a decimal one.

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Andrew Starks


On Saturday, August 10, 2013, Luiz Henrique de Figueiredo wrote:
> Given that this subject comes up every couple of months, and that there are some subtleties to it, that this warrants a new function in the math library to do it the right way?

I don't think so. Floating-point arithmetic is not simple. I think it'd be
a disservice to try to hide its complexities in a official function that
may give the impression that it solves the problem reliably, when it can't.

On the other hand, there is nothing instrinsically wrong with floating-point
equality. The only catches come from numbers that should be equal but are
computed in two different ways and from naive expectations such as
300 * 0.7 == 21, due to a misunderstanding that floating-point representation
is a binary one, not a decimal one.

In an effort to understand (not debate), where would:

       return math.abs(lhs - rhs) < epsilon*rhs

...be unsuitable? Imagine that I'm dealing with float equality and it isn't working. Also imagine that I don't know very much about the issue.  I dig into the math library and find a function that does this. 

How am I likely to be confused further? What knowledge would I still lack that would keep me from understanding, provided that PiL offered some additional context?

As a post script, I took the advice found in Programming In Lua[1] and found myself utterly lost (although ever so slightly closer to my Calculus Through Googling degree). I read the part of the discussion related to the epsilon, but since so much of it was aimed at an audience that wasn't me, I failed to identify the solution. 

As a result, I believe that once I feel like I understand the common issues, I should (or someone like me should) write a wiki on it. I was unable to find a really good document aimed at someone who can't read calculus notation. Several Microsoft articles came very close, however. 

- Andrew
Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Victor Bombi
In case rhs < 0
----- Original Message -----
Sent: Sunday, August 11, 2013 5:19 PM
Subject: Re: Float numbers equality.



On Saturday, August 10, 2013, Luiz Henrique de Figueiredo wrote:
> Given that this subject comes up every couple of months, and that there are some subtleties to it, that this warrants a new function in the math library to do it the right way?

I don't think so. Floating-point arithmetic is not simple. I think it'd be
a disservice to try to hide its complexities in a official function that
may give the impression that it solves the problem reliably, when it can't.

On the other hand, there is nothing instrinsically wrong with floating-point
equality. The only catches come from numbers that should be equal but are
computed in two different ways and from naive expectations such as
300 * 0.7 == 21, due to a misunderstanding that floating-point representation
is a binary one, not a decimal one.

In an effort to understand (not debate), where would:

       return math.abs(lhs - rhs) < epsilon*rhs

...be unsuitable? Imagine that I'm dealing with float equality and it isn't working. Also imagine that I don't know very much about the issue.  I dig into the math library and find a function that does this. 

How am I likely to be confused further? What knowledge would I still lack that would keep me from understanding, provided that PiL offered some additional context?

As a post script, I took the advice found in Programming In Lua[1] and found myself utterly lost (although ever so slightly closer to my Calculus Through Googling degree). I read the part of the discussion related to the epsilon, but since so much of it was aimed at an audience that wasn't me, I failed to identify the solution. 

As a result, I believe that once I feel like I understand the common issues, I should (or someone like me should) write a wiki on it. I was unable to find a really good document aimed at someone who can't read calculus notation. Several Microsoft articles came very close, however. 

- Andrew
Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Dirk Laurie-2
2013/8/11 Victor Bombi <[hidden email]>:

> In case rhs < 0

True, but one we know what Andrew means.

> ----- Original Message -----
> From: Andrew Starks
>
> In an effort to understand (not debate), where would:
>
>        return math.abs(lhs - rhs) < epsilon*rhs
>
> ...be unsuitable?

Suppose lhs and rhs are both sums of many terms of both signs, and
the total is close to zero, in theory might even be equal to zero.
The above test may well not be satisfied when actually the numbers
are equal on paper. Instead, one should be using

     return math.abs(lhs - rhs) < epsilon*scale

where 'scale' is a rough approximation to the sums of the absolute
values.

There are other situations. You can't fix epsilon by looking at
machine precision: the more computations it takes to compute the
operands, the bigger epsilon should be, etc.

Luiz is right here: the appropriate test for floating-point equality
is never so simple that any one method is always foolproof. There is
no substitute for consulting an experienced numerical analyst. :-)

Reply | Threaded
Open this post in threaded view
|

Re: Float numbers equality.

Coda Highland
In reply to this post by Victor Bombi
> ----- Original Message -----
> From: Andrew Starks
> To: Lua mailing list
> Sent: Sunday, August 11, 2013 5:19 PM
> Subject: Re: Float numbers equality.
>
>
>
> On Saturday, August 10, 2013, Luiz Henrique de Figueiredo wrote:
>>
>> > Given that this subject comes up every couple of months, and that there
>> > are some subtleties to it, that this warrants a new function in the math
>> > library to do it the right way?
>>
>> I don't think so. Floating-point arithmetic is not simple. I think it'd be
>> a disservice to try to hide its complexities in a official function that
>> may give the impression that it solves the problem reliably, when it
>> can't.
>>
>> On the other hand, there is nothing instrinsically wrong with
>> floating-point
>> equality. The only catches come from numbers that should be equal but are
>> computed in two different ways and from naive expectations such as
>> 300 * 0.7 == 21, due to a misunderstanding that floating-point
>> representation
>> is a binary one, not a decimal one.
>
>
> In an effort to understand (not debate), where would:
>
>        return math.abs(lhs - rhs) < epsilon*rhs
>
> ...be unsuitable? Imagine that I'm dealing with float equality and it isn't
> working. Also imagine that I don't know very much about the issue.  I dig
> into the math library and find a function that does this.
>
> How am I likely to be confused further? What knowledge would I still lack
> that would keep me from understanding, provided that PiL offered some
> additional context?
>
> As a post script, I took the advice found in Programming In Lua[1] and found
> myself utterly lost (although ever so slightly closer to my Calculus Through
> Googling degree). I read the part of the discussion related to the epsilon,
> but since so much of it was aimed at an audience that wasn't me, I failed to
> identify the solution.
>
> As a result, I believe that once I feel like I understand the common issues,
> I should (or someone like me should) write a wiki on it. I was unable to
> find a really good document aimed at someone who can't read calculus
> notation. Several Microsoft articles came very close, however.
>
> - Andrew
> [1] http://www.fer.unizg.hr/_download/repository/paper%5B1%5D.pdf

On Sun, Aug 11, 2013 at 9:03 AM, Victor Bombi <[hidden email]> wrote:
> In case rhs < 0

For the sake of having an actually productive discussion, let's assume
he ACTUALLY meant the bugfixed version:

return math.abs(lhs - rhs) < math.abs(epsilon * rhs)

The expression itself is never really WRONG (if you're paranoid you
might replace the < with a <= to handle underflow in the epsilon*rhs
term, but more on this later), but there are cases when direct
floating-point equality works just fine and the expression is a
comparatively-expensive waste of time.

The real problem is in understanding what a suitable epsilon really is
and how to minimize the error in the calculation. This requires an
understanding of your specific problem domain and gets into analysis
of the behavior of rounding error of floating-point operations. A
*single* addition/subtraction won't introduce relative error greater
than IIRC 2^-52 (maybe 2^-51? I forget exactly), while a
multiplication or division can introduce more, and more complicated
expressions like logarithm or square root could really throw the whole
thing off if you do it wrong.

Long story short, this expression is often the right thing to do, but
it's meaningless to do it if your calculation leading into it triggers
nasty rounding errors. Proper understanding of how to rearrange a FP
calculation to minimize error is far more important than the actual
epsilon comparison.

/s/ Adam

123