float equality

spir denis.spir at gmail.com
Sat Feb 19 11:54:21 PST 2011


On 02/19/2011 04:40 PM, Don wrote:
> spir wrote:
>> On 02/19/2011 01:21 PM, Jonathan M Davis wrote:
>>> On Saturday 19 February 2011 04:06:38 spir wrote:
>>>> Hello,
>>>>
>>>> What do you think of this?
>>>>
>>>> unittest {
>>>> assert(-1.1 + 2.2 == 1.1); // pass
>>>> assert(-1.1 + 2.2 + 3.3 == 4.4); // pass
>>>> assert(-1.1 + 3.3 + 2.2 == 4.4); // fail
>>>> assert(-1.1 + 3.3 == 2.2); // fail
>>>> }
>>>>
>>>> There is approxEquals in stdlib, right; but shouldn't builtin "==" be
>>>> consistent anyway?
>>>
>>> == is completely consistent. The floating point values in question are _not_
>>> equal. The problem is that floating point values are inexact. There are values
>>> which a floating point _cannot_ hold, so it holds the closest value that it
>>> can.
>>> This can quickly and easily lead to having two floating points which seem like
>>> they should be equal but which aren't. == checks that they're equal, which is
>>> exactly what it should do (especially in a systems language).
>>
>> I know about inexact representation, due to bin <--> dec conversion.
>> Well, about consistency, I meant cases 2 vs 3.
>>
>>> The thing is, of course, that actual equality sucks for floating point values.
>>> What's really needed in something like approxEquals. So, approxEquals really
>>> _should_ be used for comparing floating points and == should only be used on
>>> them
>>> when that is actually what you need (which would be extremely rare). However,
>>> you can't really make == do approxEquals, because that means that == isn't
>>> really exact equality anymore and because then it makes it hard to actually
>>> compare floating point values for equality if that's what you actually want.
>>> Making == do approxEquals instead would make == inconsistent, not consistent.
>>>
>>> This is just one of those cases where the programmer needs to know what they're
>>> doing and not use ==.
>>
>> Right, that's what I thought. I guess what people mean in 99% cases is
>> "conceptual" equality, right?
>
>> They don't care about internal representation if they are not forced to. So,
>> maybe, default '==' should perform approxEqual if needed?
>
> Approximate equality is not problem-free, either. You still need to know what
> you're doing. Regardless of what tolerance you use, there will be values which
> are just inside and just outside the tolerance, which in pure mathematics would
> be equal to each other.
>
> And we may have a binEqual just for
>> floats when someone really needs binary repr equality? (I cannot impagine a
>> use case, but well...)
>
> There are actually a lot of use cases. Most obviously in unit tests, where
> you're testing for exact rounding. There is only one correct answer.
>
>> What do you think?
>>
>> I any case, what value should one use as tolerance range for approxEqual?
>
> That's exactly the problem! There is no good universal default value.
>
> My very first contribution to D was a floating point function called
> approxEqual, but someone insisted that it be called 'feqrel'. Which has pretty
> much ensured that everyone misses it. :(
> It defines approximate equality in the correct way, in terms of numbers of
> significant bits. Basically, every time you have a multiply, you lose one bit.
> Addition can lose multiple bits.

Man, but why do people chose names like that? Isn't programming hard enough 
that people think we must add difficulty on difficulty?

Right, anyway, I think that's what I'm looking for.
Doc says:

int feqrel(X)(X x, X y);
     To what precision is x equal to y?

     Returns:
     the number of mantissa bits which are equal in x and y.
     eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision.

     Special Values
     x 	y 	feqrel(x, y)
     -------------------------
     x 	x 	real.mant_dig
     x 	>= 2*x 	0
     x 	<= x/2 	0
     NAN any 	0
     any NAN 	0

How is it intended to be used? Is the user supposed to count the number of 
(expected) remaining correct bits, depending on kind and number of operations?

Note that I'm aware of the issues caused by binary representations. But I 
thought there were well-known methods to deal with them; and that modern 
languages would implement them ;-).
In particuliar, I imagined there would be a way to somehow detect (thank to 
increased internal precision) that an intermediate or final result is actually 
(exactly/mathematically) equal to a finite precision decimal number, while its 
binary representation is approximate. (I know some languages use some heuristic 
for that, intended for string output, so that one gets eg '0.1' instead of, 
say, '0.09999999999997').
FLoats are an infinite source of mess... We definitely need decimals, don't we; 
or maybe rationals?
(I will let floats alone, waiting for processor manufacturors to implement 
decimal computation in hw.)

Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d mailing list