Thread overview
Float Comparison Returns False
Jul 07, 2011
Loopback
Jul 07, 2011
Simen Kjaeraas
Jul 08, 2011
Loopback
Jul 08, 2011
bearophile
Jul 08, 2011
Jonathan M Davis
Jul 08, 2011
Loopback
Jul 09, 2011
Loopback
Jul 09, 2011
bearophile
Jul 09, 2011
David Nadlinger
July 07, 2011
Hello!

I've been programming some miscellaneous code and got stuck in an odd
case. While comparing floats, two obviously identical values return
false in comparison.

I am not sure if this is related to float precision or something
similar. This is the code that I have used:

import std.stdio;

void main(string[] args)
{
	while(foo()) {}
}

bool foo()
{
	static bool ss;
	static int loops;
	static float m = 0f;
	
	if(m != 1.73205f)
	{
		m += 0.00500592f;
		
		if(++loops == 346)
			ss = true;
	}

	if(ss)
	{
		writefln("Variable: %s", m);
		writefln("Constant: %s", 1.73205f);
		writefln("Equality: %s", m == 1.73205f);
		
		return false;
	}
	
	return true;
}

The output of this program is the following:

Variable: 1.73205
Constant: 1.73205
Equality: false

My question is; how come these values compare unequal?
July 07, 2011
On Thu, 07 Jul 2011 20:19:05 +0200, Loopback <elliott.darfink@gmail.com> wrote:

> Hello!
>
> I've been programming some miscellaneous code and got stuck in an odd
> case. While comparing floats, two obviously identical values return
> false in comparison.
>
> I am not sure if this is related to float precision or something
> similar. This is the code that I have used:
>
> import std.stdio;
>
> void main(string[] args)
> {
> 	while(foo()) {}
> }
>
> bool foo()
> {
> 	static bool ss;
> 	static int loops;
> 	static float m = 0f;
> 	
> 	if(m != 1.73205f)
> 	{
> 		m += 0.00500592f;
> 		
> 		if(++loops == 346)
> 			ss = true;
> 	}
>
> 	if(ss)
> 	{
> 		writefln("Variable: %s", m);
> 		writefln("Constant: %s", 1.73205f);
> 		writefln("Equality: %s", m == 1.73205f);
> 		
> 		return false;
> 	}
> 	
> 	return true;
> }
>
> The output of this program is the following:
>
> Variable: 1.73205
> Constant: 1.73205
> Equality: false
>
> My question is; how come these values compare unequal?

Try adding this in there:

  writefln("Difference: %s", m - 1.73205);

It prints:
Difference: 1.61095e-06


It may also be worth using %a to see the actual values in a float:

  writefln("Variable: %a", m);
  writefln("Constant: %a", 1.73205f);

Variable: 0x1.bb67bcp+0
Constant: 0x1.bb67ap+0

As you can see, these numbers are different. Floating point math
is weird. Two numbers that look the same can be different.
-- 
  Simen
July 07, 2011
On Thu, 07 Jul 2011 14:19:05 -0400, Loopback <elliott.darfink@gmail.com> wrote:

> Hello!
>
> I've been programming some miscellaneous code and got stuck in an odd
> case. While comparing floats, two obviously identical values return
> false in comparison.
>
> I am not sure if this is related to float precision or something
> similar. This is the code that I have used:
>
> import std.stdio;
>
> void main(string[] args)
> {
> 	while(foo()) {}
> }
>
> bool foo()
> {
> 	static bool ss;
> 	static int loops;
> 	static float m = 0f;
> 	
> 	if(m != 1.73205f)
> 	{
> 		m += 0.00500592f;
> 		
> 		if(++loops == 346)
> 			ss = true;
> 	}
>
> 	if(ss)
> 	{
> 		writefln("Variable: %s", m);
> 		writefln("Constant: %s", 1.73205f);
> 		writefln("Equality: %s", m == 1.73205f);
> 		
> 		return false;
> 	}
> 	
> 	return true;
> }
>
> The output of this program is the following:
>
> Variable: 1.73205
> Constant: 1.73205
> Equality: false
>
> My question is; how come these values compare unequal?

Because they aren't.  Just because they are equal to 5 decimal places (which by the way is an inaccurate printout of their value), does not mean they are fully equal.

Be very careful when comparing floating point numbers.  Generally you want to use an epsilon to say they are "close enough".

-Steve
July 08, 2011
On 2011-07-07 20:35, Steven Schveighoffer wrote:
> On Thu, 07 Jul 2011 14:19:05 -0400, Loopback <elliott.darfink@gmail.com>
> wrote:
>
>> Hello!
>>
>> I've been programming some miscellaneous code and got stuck in an odd
>> case. While comparing floats, two obviously identical values return
>> false in comparison.
>>
>> I am not sure if this is related to float precision or something
>> similar. This is the code that I have used:
>>
>> import std.stdio;
>>
>> void main(string[] args)
>> {
>> while(foo()) {}
>> }
>>
>> bool foo()
>> {
>> static bool ss;
>> static int loops;
>> static float m = 0f;
>>
>> if(m != 1.73205f)
>> {
>> m += 0.00500592f;
>>
>> if(++loops == 346)
>> ss = true;
>> }
>>
>> if(ss)
>> {
>> writefln("Variable: %s", m);
>> writefln("Constant: %s", 1.73205f);
>> writefln("Equality: %s", m == 1.73205f);
>>
>> return false;
>> }
>>
>> return true;
>> }
>>
>> The output of this program is the following:
>>
>> Variable: 1.73205
>> Constant: 1.73205
>> Equality: false
>>
>> My question is; how come these values compare unequal?
>
> Because they aren't. Just because they are equal to 5 decimal places
> (which by the way is an inaccurate printout of their value), does not
> mean they are fully equal.
>
> Be very careful when comparing floating point numbers. Generally you
> want to use an epsilon to say they are "close enough".
>
> -Steve

Thank you for your answers!

I do want to ask though what an alternative would be in this case, to
compare the two different values. You mentioned something about
"epsilons" but I have no experience within this field. I would really
appreciate an example or something similar so I could understand your
statement.

From what I can see these are two identical values, I would be more than
glad if someone could explain just what is the difference between these
two "non-equal" values and how make them "equal". Perhaps I should use
ints and long instead since they don't seem to suffer from this
"problem"?
July 08, 2011
Loopback:

> I do want to ask though what an alternative would be in this case, to compare the two different values. You mentioned something about "epsilons" but I have no experience within this field. I would really appreciate an example or something similar so I could understand your statement.

If you want to use floating point values in your programs then you probably need to know something about floating point representation. This is a good starting point:
http://en.wikipedia.org/wiki/Floating_point
For your problem there is the feqrel function:
http://www.digitalmars.com/d/2.0/phobos/std_math.html#feqrel


>  From what I can see these are two identical values, I would be more than
> glad if someone could explain just what is the difference between these
> two "non-equal" values and how make them "equal". Perhaps I should use
> ints and long instead since they don't seem to suffer from this
> "problem"?

Generally in a program you use floating point values only if you can't use integral values (and you don't want to go toward fixed point values, rationals, etc).

Bye,
bearophile
July 08, 2011
On 2011-07-07 17:28, bearophile wrote:
> Loopback:
> > I do want to ask though what an alternative would be in this case, to compare the two different values. You mentioned something about "epsilons" but I have no experience within this field. I would really appreciate an example or something similar so I could understand your statement.
> 
> If you want to use floating point values in your programs then you probably
> need to know something about floating point representation. This is a good
> starting point: http://en.wikipedia.org/wiki/Floating_point
> For your problem there is the feqrel function:
> http://www.digitalmars.com/d/2.0/phobos/std_math.html#feqrel
> 
> > From what I can see these are two identical values, I would be more than
> > 
> > glad if someone could explain just what is the difference between these two "non-equal" values and how make them "equal". Perhaps I should use ints and long instead since they don't seem to suffer from this "problem"?
> 
> Generally in a program you use floating point values only if you can't use integral values (and you don't want to go toward fixed point values, rationals, etc).

What Every Computer Scientist Should Know About Floating-Point Arithmetic:

http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html

- Jonathan M Davis
July 08, 2011
On 2011-07-08 02:28, bearophile wrote:
> Loopback:
>
>> I do want to ask though what an alternative would be in this case, to
>> compare the two different values. You mentioned something about
>> "epsilons" but I have no experience within this field. I would really
>> appreciate an example or something similar so I could understand your
>> statement.
>
> If you want to use floating point values in your programs then you probably need to know something about floating point representation. This is a good starting point:
> http://en.wikipedia.org/wiki/Floating_point
> For your problem there is the feqrel function:
> http://www.digitalmars.com/d/2.0/phobos/std_math.html#feqrel
>
>
>>    From what I can see these are two identical values, I would be more than
>> glad if someone could explain just what is the difference between these
>> two "non-equal" values and how make them "equal". Perhaps I should use
>> ints and long instead since they don't seem to suffer from this
>> "problem"?
>
> Generally in a program you use floating point values only if you can't use integral values (and you don't want to go toward fixed point values, rationals, etc).
>
> Bye,
> bearophile
I've looked up both approxEqual and feqrel, and the last one seems most
appropriate, though I cannot use this function without issuing any
errors. If I use the following code:

writefln("Equality: %s", feqrel(m, 1.73205f));

(From the previous example)

I receive this error with the code:
Error: function std.math.feqrel!(float).feqrel has no return statement, but is expected to return a value of type int
Error: template instance std.math.feqrel!(float) error instantiating
July 09, 2011
On 2011-07-08 04:31, Loopback wrote:
> On 2011-07-08 02:28, bearophile wrote:
>> Loopback:
>>
>>> I do want to ask though what an alternative would be in this case, to
>>> compare the two different values. You mentioned something about
>>> "epsilons" but I have no experience within this field. I would really
>>> appreciate an example or something similar so I could understand your
>>> statement.
>>
>> If you want to use floating point values in your programs then you
>> probably need to know something about floating point representation.
>> This is a good starting point:
>> http://en.wikipedia.org/wiki/Floating_point
>> For your problem there is the feqrel function:
>> http://www.digitalmars.com/d/2.0/phobos/std_math.html#feqrel
>>
>>
>>> From what I can see these are two identical values, I would be more than
>>> glad if someone could explain just what is the difference between these
>>> two "non-equal" values and how make them "equal". Perhaps I should use
>>> ints and long instead since they don't seem to suffer from this
>>> "problem"?
>>
>> Generally in a program you use floating point values only if you can't
>> use integral values (and you don't want to go toward fixed point
>> values, rationals, etc).
>>
>> Bye,
>> bearophile
> I've looked up both approxEqual and feqrel, and the last one seems most
> appropriate, though I cannot use this function without issuing any
> errors. If I use the following code:
>
> writefln("Equality: %s", feqrel(m, 1.73205f));
>
> (From the previous example)
>
> I receive this error with the code:
> Error: function std.math.feqrel!(float).feqrel has no return statement,
> but is expected to return a value of type int
> Error: template instance std.math.feqrel!(float) error instantiating
I weren't able to solve this error so if it's of anyone's interest I
used this function instead;

int feqrel(real a, real b)
{
    if (a==b) return real.mant_dig;
    real diff = fabs(a-b);

    ushort *pa = cast(ushort *)(&a);
    ushort *pb = cast(ushort *)(&b);
    ushort *pd = cast(ushort *)(&diff);

    int bitsdiff = ( ((pa[4]&0x7FFF) + (pb[4]&0x7FFF)-1)>>1) - pd[4];

    if (pd[4]== 0)
    {
	   diff*=0x1p+63;
	   return bitsdiff + real.mant_dig - pd[4];
    }
    if (bitsdiff>0) return bitsdiff+1;
    return bitsdiff==0 ? pa[4]==pb[4] : 0;
}

From: http://www.digitalmars.com/d/archives/digitalmars/D/27873.html
July 09, 2011
Loopback:

> I weren't able to solve this error so if it's of anyone's interest I used this function instead;

If you think you have found a bug in Phobos, then I suggest you to add it to Bugzilla (with your working version too, if you want).

Bye,
bearophile
July 09, 2011
This has already been reported: http://d.puremagic.com/issues/show_bug.cgi?id=5089

David


On 7/9/11 2:01 PM, bearophile wrote:
> Loopback:
>
>> I weren't able to solve this error so if it's of anyone's interest I
>> used this function instead;
>
> If you think you have found a bug in Phobos, then I suggest you to add it to Bugzilla (with your working version too, if you want).
>
> Bye,
> bearophile