Always false float comparisons

Ola Fosheim Grøstad via Digitalmars-d digitalmars-d at puremagic.com
Tue May 17 22:49:16 PDT 2016


On Wednesday, 18 May 2016 at 03:01:14 UTC, Joakim wrote:
> There is nothing "random" about increasing precision till the 
> end, it follows a well-defined rule.

Can you please quote that well-defined rule?

It is indeed random, or arbitrary (which is the same thing):

if(x<0){
   // DMD choose 64 bit mantissa
   const float y = ...
   ...

} else {
   // DMD choose 24 bit mantissa
   float y = ...
   ...
}

How is this not arbitrary?

If x is the amplitude then a flaw like this can cause a DC offset 
to accumulate and you end up with reduced precision, not improved 
precision. A DC filter at the end does not help on this precision 
loss.

> So you're planning on running phase-locking code partially in 
> CTFE and runtime and it's somehow very sensitive to precision?  
> If your "phase-locking" depends on producing bit-exact results 
> with floating-point, you're doing it wrong.

I am not doing anything wrong. D is doing it wrong. If you add 
different deltas then you will get drift. So, no improved 
precision in calculating some deltas is not improving the 
accuracy. It makes it worse.

> If any of this depends on comparing bit-exact floating-point 
> results, you're doing it wrong.

It depends on the unit tests running with the exact same 
precision as the production code.

Fast floating point code depends on the specifics of the 
hardware. A system level language should not introduce a 
different kind of bias that isn't present in the hardware!

D is doing it wrong because it makes it is thereby forcing 
programmers to use algorithms that are 10-100x slower to get 
reliable results.

That is _wrong_.

> If the constant is calculated rather than a literal, you should 
> be checking using approxEqual.

No. 1+2+3+4 is exact on all floating point units I know of.

>> 3. Comparing for equality is the same as subtraction followed 
>> by testing for zero.
>
> So what?  You should be using approxEqual.

No.

>> So, the rule is: you shouldn't compare at all unless you know 
>> the error bounds, but that depends on WHY you are comparing.
>
> No, you should always use error bounds.  Sometimes you can get 
> away with checking bit-exact equality, say for constants that 
> you defined yourself with no calculation, but it's never a good 
> practice.

It is if you know WHY you are doing a test.

Btw, have you ever tried to prove error bounds for an iterative 
method?
You actually think most people prove them to be correct?

Or perhaps almost all of them just pick a number out of thin air 
which they think will work out and rely on testing their code?

Well, the latter is no better than checking for exact equality. 
And I can assure you that the vast majority of programmers do not 
prove error bounds with the level of rigour it takes to get it 
correct.

The reality is that it is common practice to write code that 
seems to work. But that does not make it correct. However, making 
it correct is way too time consuming and often not worth the 
trouble. So people rely on testing. Floating point code is no 
different.

But with D semantics you cannot rely on testing. That's bad, 
because most people write incorrect code. Whether they are 
experts or not. (it is only matter of difference in frequency)

>> f(x) = 1/(2-x)
>>
>> Should I not be able to test for the exact value "2" here?
>
> It would make more sense to figure out what the max value of 
> f(x) is you're trying to avoid, say 1e6, and then check for 
> approxEqual(x, 2, 2e-6).  That would make much more sense than 
> only avoiding 2, when an x that is arbitrarily close to 2 can 
> also blow up f(x).

I am trying to avoid an exception, not a value.

> Oh, it's real world alright, you should be avoiding more than 
> just 2 in your example above.

Which number would that be?

> Simply repeating the word "random" over and over again does not 
> make it so.

That's right. It is DMD that makes it so, not my words.

However, in order to reject what other say, you have to make an 
argument. And in this case we have:

1. A system level programming language that claims to excel at 
floating point.
2. Hardware platforms with specific behaviour.

Unfortunately 2 is true, but not 1.

D is not matching up to the minimum requirements for people 
wanting to write fast and reliable floating point code for a 
given platform.

According to the D spec, the compiler could schedule typed single 
precision floating point calculations to two completely different 
floating point units (and yes, there are platforms that provide 
multiple incompatible floating point units with widely differing 
characteristics).

That is random.
And so is "float" behaving differently than "const float".



More information about the Digitalmars-d mailing list