[cfe-dev] [libcxx/test] Floating point comparisons

Edward Meewis ed at extraordinarymachine.nl
Thu Dec 1 12:52:44 PST 2011


On 01-12-11 20:41, Richard Smith wrote:
> On Thu, December 1, 2011 19:09, Howard Hinnant wrote:
>> On Dec 1, 2011, at 1:54 PM, Edward Meewis wrote:
>>> I am experimenting with libcxx on FreeBSD and got a lot of asserts on
>>> floating point comparisons, most notably in 'libcxx/test/containers/unord',
>>> e.g.:
>>>
>>>
>>> Assertion failed: (c.load_factor() == (float)c.size()/c.bucket_count()),
>>> function main, file assign_move.pass.cpp, line 71.
>>>
>>> Switching on optimization makes them go away. I'm guessing one of the
>>> values gets promoted from a float to a double somewhere, which makes the
>>> comparison fail.
>>>
>>> The question is: do the lvalue and rvalue need to be exactly the same? I
>>> think it would be better to use something like:
>>>
>>> assert(fabs(c.load_factor() - (float)c.size()/c.bucket_count())<
>>> FLT_EPSILON);
>>>
>>>
>>> to sidestep the issue. It also has the benefit of being more implementation
>>> independent?
>> Before changing this I'd like to understand the issue, just to make sure we
>> wouldn't be hiding a bug.
>>
>> load_factor() is computed (in<__hash_table>) as:
>>
>> _LIBCPP_INLINE_VISIBILITY float load_factor() const _NOEXCEPT
>> {
>> size_type __bc = bucket_count(); return __bc != 0 ? (float)size() / __bc : 0.f;
>>   }
>>
>>
>> which is precisely how the test is computed too.  So I currently do not
>> understand the cause of the assert.
>>
>> And just to head off the discussion that one should never compare floating
>> point with equality:  I'm aware of how floating point works.  But I also know
>> that it is deterministic, down to the very last bit.
> Not so fast. See C++98[expr]p10 / C++11[expr]p11, "The values of the
> floating operands and the results offloating expressions may be
> represented in greater
> precision and range than that required by the type". There's no requirement
> that the compiler do this in a consistent way, or even do it the same way
> every time the same expression is evaluated.
>
> (In practice, the result of the computation on, say, x86 will depend on
> whether the computation is performed using the 80-bit x87 FPU registers or one
> of the more modern 64-bit registers, whether extended precision is enabled,
> which rounding mode is set, whether FPU 80-bit registers got spilled to 64-bit
> stack slots, etc.)
>
> Richard
>
Wow, that's very helpful, thanks. Things get dodgy when working with 
floats and doubles.

Well, I did some further digging and things got weird:

 > cat float.c

#include <stdio.h>
#include <float.h>

int main(void) {

         int a = 4;
         int b = 7;
         float f = (float) a/b;
         double d = (double) a/b;

         printf("%e\n", f - (float)a/b );
         printf("%e\n", d - (float)a/b );
}

 > clang float.c && ./a.out
2.554485e-08
0.000000e+00

 > clang -O2 float.c && ./a.out
0.000000e+00
-2.554485e-08

 > clang float.c -S -o float.S
(.....)
     movl    $.L.str, (%ecx)
     movl    %eax, -56(%ebp)         # 4-byte Spill
     calll   printf
(.....)

I guess that indicates you're right. Full asm is attached for the 
curious. I can't determine if what happens is actually right.

Hope it is helpful to some-one, Ed.

PS. The processor is an Atom330, so not your regular run of the mill....

-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: float.S
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20111201/fb4c07d8/attachment.ksh>


More information about the cfe-dev mailing list