Float comparison

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Float comparison

Richard Wettel-2
Hi,

I am trying to implement some SUnit tests and I need to compare float  
(or double) values.
Even when the two values compared are equal, I still get an  
AssertionFailed error.

I could define a very small float and assert the absolute value of  
the difference between the two numbers to be less than this number,  
but I do not like this solution.
Is there a workaround for this?

Thanks,
Richard Wettel



Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

Dennis smith-4
You say "they are equal", but floats arrived at from different points of
view
(different calculations) may not be equal.

For example
    4.0 / 2.0 = 2.0
may result in false.

Floats are inherently approximations, so your solution of comparing to a
small value is a possibility.
You might want to say "it has to be within 0.1% or something like that.

I always wondered why there was not an "almostequal" method.



Richard Wettel wrote:

> Hi,
>
> I am trying to implement some SUnit tests and I need to compare float
> (or double) values.
> Even when the two values compared are equal, I still get an
> AssertionFailed error.
>
> I could define a very small float and assert the absolute value of the
> difference between the two numbers to be less than this number, but I
> do not like this solution.
> Is there a workaround for this?
>
> Thanks,
> Richard Wettel
>
>
>

--
Dennis Smith                        [hidden email]
Cherniak Software Development Corporation       +1 905.771.7011
400-10 Commerce Valley Dr E                Fax: +1 905.771.6288
Thornhill, ON Canada L3T 7N7    http://www.CherniakSoftware.com

Reply | Threaded
Open this post in threaded view
|

RE: Float comparison

Thomas Brodt
> -----Original Message-----
> From: Dennis Smith [mailto:[hidden email]]
> Sent: Thursday, November 16, 2006 2:12 PM
> To: vwnc >> "VWNC, "
> Subject: Re: Float comparison
>
> You say "they are equal", but floats arrived at from
> different points of
> view
> (different calculations) may not be equal.
>
> For example
>     4.0 / 2.0 = 2.0
> may result in false.
>
> Floats are inherently approximations, so your solution of
> comparing to a
> small value is a possibility.
> You might want to say "it has to be within 0.1% or something
> like that.
>
> I always wondered why there was not an "almostequal" method.

I agree, but you would need fuzzy logic for this to determine almost :)
I also miss something like #equals:precision: in Number with all that double dispatching to different number types (Integers,
FixedPoints, Float, Doubles, etc)
e.g.
(2.19999 equals: 2.2 precision 0.01) == true
I would need that, too.

Thomas

Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

Mike Hales
In reply to this post by Richard Wettel-2
I defined my own TestCase>>closeEnough:to:  that asserts that one is  
close enough to another.  The precision of the check is higher for  
Doubles than for Floats.  Rather than assert: in my test cases I just  
call closeEnough: aFloat to: anotherFloat.

Mike

Mike Hales
Engineering Manager
KnowledgeScape
(801)413-2809
(801)880-6270 Fax
[hidden email]



On Nov 16, 2006, at 8:07 AM, Richard Wettel wrote:

> Hi,
>
> I am trying to implement some SUnit tests and I need to compare  
> float (or double) values.
> Even when the two values compared are equal, I still get an  
> AssertionFailed error.
>
> I could define a very small float and assert the absolute value of  
> the difference between the two numbers to be less than this number,  
> but I do not like this solution.
> Is there a workaround for this?
>
> Thanks,
> Richard Wettel
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

Reinout Heeck
In reply to this post by Richard Wettel-2
Richard Wettel wrote:
>
> I am trying to implement some SUnit tests and I need to compare  
> float (or double) values.
> Even when the two values compared are equal, I still get an  
> AssertionFailed error.

One problem with floats is that what you see is not what you get:  
when converting between what you see (decimal) and what you get  
(binary) you can get small conversion errors. As you have experienced  
you can have two floats that look the same (decimal) but aren't  
(binary).

> I could define a very small float and assert the absolute value of  
> the difference between the two numbers to be less than this number,  
> but I do not like this solution.

Using a fudge factor is a common solution that will probably do what  
you need in the context of unit tests.
I recall that the Numerical Methods bundle in the pub repository  
contains a test to measure the precision of the float implementation,  
you could run that test to derive your fudge factor.


> Is there a workaround for this?

Well, given the above you could try to specify the expected float  
values precisely (binary, not decimal) in your unit tests.
You can get to the binary representation with something like:

     0.5 copy changeClassTo: ByteArray

and in the other direction:

    #[0 0 0 63] copy changeClassTo: Float



However for the case where the decimal representation of two floats  
looks the same there is a simpler solution:
compare the #displayString of the two floats instead of their value.


HTH,

Reinout
-------

Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

Reinout Heeck
>
>> I could define a very small float and assert the absolute value of  
>> the difference between the two numbers to be less than this  
>> number, but I do not like this solution.
>
> Using a fudge factor is a common solution that will probably do  
> what you need in the context of unit tests.


I forgot to mention that applying a fudge to a float value comparison  
is at odds with what a float does: you'll need bigger fudges for  
bigger values.

So it is probably better to apply the fudge to only the mantissa of  
the float instead of to the float itself.


R
-

Reply | Threaded
Open this post in threaded view
|

RE: Float comparison

James Foster-3
In reply to this post by Richard Wettel-2
I've added the following to some TestCase subclasses:

is: a equivalentTo: b

        | difference epsilon answer |
        epsilon := (a abs max: b abs) * 1.0e-08.
        difference := (a - b) abs asScaledDecimal: 12.
        answer := difference <= epsilon.
        ^answer.

-----Original Message-----
From: Richard Wettel [mailto:[hidden email]]
Sent: Thursday, November 16, 2006 5:07 AM
To: vwnc-list
Subject: Float comparison

Hi,

I am trying to implement some SUnit tests and I need to compare float  
(or double) values.
Even when the two values compared are equal, I still get an  
AssertionFailed error.

I could define a very small float and assert the absolute value of  
the difference between the two numbers to be less than this number,  
but I do not like this solution.
Is there a workaround for this?

Thanks,
Richard Wettel



Reply | Threaded
Open this post in threaded view
|

RE: Float comparison

mark.b.ballard
This might not be as helpful as the practical advice you've already
gotten, but I recommend that you read the classic article

What Every Computer Scientist Should Know About Floating-Point
Arithmetic

  http://docs.sun.com/source/806-3568/ncg_goldberg.html



-----Original Message-----
From: James Foster [mailto:[hidden email]]
Sent: Thursday, November 16, 2006 8:01 AM
To: 'vwnc-list'
Subject: RE: Float comparison


I've added the following to some TestCase subclasses:

is: a equivalentTo: b

        | difference epsilon answer |
        epsilon := (a abs max: b abs) * 1.0e-08.
        difference := (a - b) abs asScaledDecimal: 12.
        answer := difference <= epsilon.
        ^answer.

-----Original Message-----
From: Richard Wettel [mailto:[hidden email]]
Sent: Thursday, November 16, 2006 5:07 AM
To: vwnc-list
Subject: Float comparison

Hi,

I am trying to implement some SUnit tests and I need to compare float  
(or double) values.
Even when the two values compared are equal, I still get an  
AssertionFailed error.

I could define a very small float and assert the absolute value of  
the difference between the two numbers to be less than this number,  
but I do not like this solution.
Is there a workaround for this?

Thanks,
Richard Wettel



Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

Richard Wettel-2
In reply to this post by Richard Wettel-2
Hi again,
Thanks for all the answers.

Richard Wettel

Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

Travis Griggs-3
In reply to this post by James Foster-3
On Nov 16, 2006, at 8:01, James Foster wrote:

I've added the following to some TestCase subclasses:

is: a equivalentTo: b

| difference epsilon answer |
epsilon := (a abs max: b abs) * 1.0e-08.
difference := (a - b) abs asScaledDecimal: 12.
answer := difference <= epsilon.
^answer.

We did something like this at Siemens. We didn't use the ScaledDecimal part. But you can basically put all this behavior on LimitedPrecisionReal and have a method called "approximates:". And then the receiver can return it's "accuracy" (i.e. 7.5ish for Floats and 14.something for Doubles). And if you really wanna go nuts, you can even write your own binary selector. Maybe ?= :)

It's of course not entirely technically correct to do this--read the paper referenced in one of the other posts--but you can actually get quite a bit of pragmatic mileage out of the approach.

--
Travis Griggs
Objologist
"Every institution finally perishes by an excess of its own first principle." - Lord Acton



Reply | Threaded
Open this post in threaded view
|

Re: Float comparison

eliot-2
In reply to this post by Richard Wettel-2
"James Foster" <[hidden email]> wrote:

| I've added the following to some TestCase subclasses:

| is: a equivalentTo: b

| | difference epsilon answer |
| epsilon := (a abs max: b abs) * 1.0e-08.
| difference := (a - b) abs asScaledDecimal: 12.
| answer := difference <= epsilon.
| ^answer.

what's wrong with this is that it doesn;t say "close enough to depends on te size of the numbers".  e.g. 100,000,001 is quite close to 100,000,000, but 1.5 is not very close to 0.5.  You want something like

        a abs - b abs / (a abs + b abs) < 1e-8

and eliminate zero by
        a = b or: [a abs - b abs / (a abs + b abs) < 2d-8]

| -----Original Message-----
| From: Richard Wettel [mailto:[hidden email]]
| Sent: Thursday, November 16, 2006 5:07 AM
| To: vwnc-list
| Subject: Float comparison

| Hi,

| I am trying to implement some SUnit tests and I need to compare float
| (or double) values.
| Even when the two values compared are equal, I still get an
| AssertionFailed error.

| I could define a very small float and assert the absolute value of
| the difference between the two numbers to be less than this number,
| but I do not like this solution.
| Is there a workaround for this?

| Thanks,
| Richard Wettel
---
Eliot Miranda                 ,,,^..^,,,                mailto:[hidden email]
VisualWorks Engineering, Cincom  Smalltalk: scene not herd  Tel +1 408 216 4581
3350 Scott Blvd, Bldg 36 Suite B, Santa Clara, CA 95054 USA Fax +1 408 216 4500


Reply | Threaded
Open this post in threaded view
|

RE: Float comparison

James Foster-3
The 'epsilon' is calculated based on the size of the numbers, so your first
example returns true while your second example returns false.

-----Original Message-----
From: [hidden email] [mailto:[hidden email]]
Sent: Thursday, November 16, 2006 10:38 AM
To: [hidden email]
Cc: [hidden email]
Subject: Re: Float comparison

"James Foster" <[hidden email]> wrote:

| I've added the following to some TestCase subclasses:

| is: a equivalentTo: b

| | difference epsilon answer |
| epsilon := (a abs max: b abs) * 1.0e-08.
| difference := (a - b) abs asScaledDecimal: 12.
| answer := difference <= epsilon.
| ^answer.

what's wrong with this is that it doesn;t say "close enough to depends on te
size of the numbers".  e.g. 100,000,001 is quite close to 100,000,000, but
1.5 is not very close to 0.5.  You want something like

        a abs - b abs / (a abs + b abs) < 1e-8

and eliminate zero by
        a = b or: [a abs - b abs / (a abs + b abs) < 2d-8]

| -----Original Message-----
| From: Richard Wettel [mailto:[hidden email]]
| Sent: Thursday, November 16, 2006 5:07 AM
| To: vwnc-list
| Subject: Float comparison

| Hi,

| I am trying to implement some SUnit tests and I need to compare float  
| (or double) values.
| Even when the two values compared are equal, I still get an  
| AssertionFailed error.

| I could define a very small float and assert the absolute value of  
| the difference between the two numbers to be less than this number,  
| but I do not like this solution.
| Is there a workaround for this?

| Thanks,
| Richard Wettel
---
Eliot Miranda                 ,,,^..^,,,
mailto:[hidden email]
VisualWorks Engineering, Cincom  Smalltalk: scene not herd  Tel +1 408 216
4581
3350 Scott Blvd, Bldg 36 Suite B, Santa Clara, CA 95054 USA Fax +1 408 216
4500


Reply | Threaded
Open this post in threaded view
|

RE: Float comparison

Steven Kelly
In reply to this post by Richard Wettel-2
> "James Foster" <[hidden email]> wrote:
>
> | I've added the following to some TestCase subclasses:
>
> | is: a equivalentTo: b
>
> | | difference epsilon answer |
> | epsilon := (a abs max: b abs) * 1.0e-08.
> | difference := (a - b) abs asScaledDecimal: 12.
> | answer := difference <= epsilon.
> | ^answer.
 
Eliot Miranda wrote:
> what's wrong with this is that it doesn;t say "close enough to depends
on
> te size of the numbers".  e.g. 100,000,001 is quite close to
100,000,000,
> but 1.5 is not very close to 0.5.  You want something like
>
> a abs - b abs / (a abs + b abs) < 1e-8

Or to avoid problems when a=-b:
(a - b) abs / (a abs + b abs) < 1e-8
 
> and eliminate zero by
> a = b or: [a abs - b abs / (a abs + b abs) < 2d-8]

Or rather, to avoid the divisor being zero:
scale := a abs + b abs.
scale isZero or: [(a - b) abs / scale < 2d-8]

What the accuracy should be depends on what you are doing with the
numbers. If you want +/- 1% for both a and b, then (a - b) abs is at
most 2%, so dividing by (a abs + b abs) - effectively 200% - nicely
means that you can just put the error you are willing to accept on the
right-hand side of the comparison. If you know a is right, and want b to
be +/- 1%, you'll need to put half the error you are willing to accept
on the right-hand side.

Steve
 
> | -----Original Message-----
> | From: Richard Wettel [mailto:[hidden email]]
> | Sent: Thursday, November 16, 2006 5:07 AM
> | To: vwnc-list
> | Subject: Float comparison
>
> | Hi,
>
> | I am trying to implement some SUnit tests and I need to compare
float

> | (or double) values.
> | Even when the two values compared are equal, I still get an
> | AssertionFailed error.
>
> | I could define a very small float and assert the absolute value of
> | the difference between the two numbers to be less than this number,
> | but I do not like this solution.
> | Is there a workaround for this?
>
> | Thanks,
> | Richard Wettel
> ---
> Eliot Miranda                 ,,,^..^,,,
> mailto:[hidden email]
> VisualWorks Engineering, Cincom  Smalltalk: scene not herd  Tel +1 408
216
> 4581
> 3350 Scott Blvd, Bldg 36 Suite B, Santa Clara, CA 95054 USA Fax +1 408
216
> 4500
>