Bug 48236

Summary: uint64_t and int64_t behave differently when converting to float
Product: gcc Reporter: Jon Valdes <juanval>
Component: targetAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED DUPLICATE    
Severity: normal CC: juanval
Priority: P3    
Version: 4.4.5   
Target Milestone: ---   
Host: Target: i?86-*-*
Build: Known to work:
Known to fail: Last reconfirmed:
Attachments: Small code to show uint64_t / int64_t difference

Description Jon Valdes 2011-03-22 12:57:58 UTC
Created attachment 23749 [details]
Small code to show uint64_t / int64_t difference

Attached code shows a comparison of converted int->float values:
( (float)(ui+1) != ((float)ui) + 1.f )

This comparison fails beginning at number 16777216 for ui of type uint64_t, but it doesn't fail there for ui of type int64_t.

Tested in gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5), Ubuntu 10.10 32 bits, atom processor. 

Compiled with no parameters: gcc test.c && ./a.out

Tested also in mingw 4.4.5, same problem.

Tested in MSVC (vs2010) too, and it doesn't fail early for uint64_t, like gcc does.
Comment 1 Jon Valdes 2011-03-22 14:08:27 UTC
Even simpler code. uint64_t and int64_t converted to floats differ beginning at 16 million.


Fails at value 16777217:
----------------------->8-----------------------
#include <stdint.h>
#include <stdio.h>

int main()
{
    uint64_t ui ;
    int64_t   i ;
    for( i=0, ui=0 ; ; i++ , ui++ )
        if( (float) i != (float) ui )
        {
            printf("error: ui=%lld\n" , ui ) ;
            break ;
        }   
    return 0 ;
 }
-----------------------8<-----------------------
Fails on default params, -O0, -O3...
Comment 2 froydnj@codesourcery.com 2011-03-22 14:18:32 UTC
Possibly an instance of PR 323.
Comment 3 Jon Valdes 2011-03-22 14:35:10 UTC
From my understanding, a positive number should be casted to exactly the same floating value representation regardless if the variable is signed or unsigned. In fact, checking the failing values, we see this:

uint64_t ui: 16777217 -> (float) ui:16777216.000000 ( ui - 1  )
uint64_t ui: 16777218 -> (float) ui:16777218.000000 ( correct )
uint64_t ui: 16777219 -> (float) ui:16777220.000000 ( ui + 1  )  

The casting of a uint64_t value to float wiggles around the correct value, but an int64_t value stays correct.

int64_t i : 16777217 -> (float) i:16777217.000000   ( correct ) 
int64_t i : 16777218 -> (float) i:16777218.000000   ( correct ) 
int64_t i : 16777219 -> (float) i:16777219.000000   ( correct )
Comment 4 Dominique d'Humieres 2011-03-22 14:50:32 UTC
> From my understanding, a positive number should be casted to exactly the same
> floating value representation regardless if the variable is signed or unsigned.
> In fact, checking the failing values, we see this:

Above 2^24 and below 2^25, only even integers are not rounded when converted to float and odd integers are rounded. I think it is what you report in comment #3.
Comment 5 Jon Valdes 2011-03-22 14:56:25 UTC
(In reply to comment #4)
> Above 2^24 and below 2^25, only even integers are not rounded when converted to
> float and odd integers are rounded. I think it is what you report in comment
> #3.

Could be, but why would it only happen for uint64_t, and not int64_t?
Comment 6 Richard Biener 2011-03-22 15:15:24 UTC
The issue seems to be a x87 specific one.  We expand the conversions
to

;; if (D.2537_5 != D.2538_6)

(insn 22 20 23 4 t.c:9 (set (reg:SF 66)
        (float:SF (reg/v:DI 62 [ i ]))) -1 (nil))

(insn 23 22 24 4 t.c:9 (set (reg:XF 68)
        (float:XF (reg/v:DI 63 [ ui ]))) -1 (nil))

(insn 24 23 25 4 t.c:9 (set (reg:CCGOC 17 flags)
        (compare:CCGOC (subreg:SI (reg/v:DI 63 [ ui ]) 4)
            (const_int 0 [0x0]))) -1 (nil))

and later

(insn 55 20 56 4 t.c:9 (parallel [
            (set (reg:SF 9 st(1) [66])
                (float:SF (reg/v:DI 1 dx [orig:62 i ] [62])))
            (clobber (mem/c:DI (plus:SI (reg/f:SI 7 sp)
                        (const_int 16 [0x10])) [0 S8 A64]))
        ]) 226 {*floatdisf2_i387_with_temp} (nil))

(insn 56 55 24 4 t.c:9 (parallel [
            (set (reg:XF 8 st [68])
                (float:XF (reg/v:DI 1 dx [orig:62 i ] [62])))
            (clobber (mem/c:DI (plus:SI (reg/f:SI 7 sp)
                        (const_int 16 [0x10])) [0 S8 A64]))
        ]) 230 {*floatdixf2_i387_with_temp} (nil))

so for some reason we miss truncation to SFmode.

We also seem to account for the fact that the used fildq instruction
operates on signed input only by compensating with the addition
of a bias - but that obviously changes rounding behavior and is not
suitable w/o -funsafe-math-optimizations (we do add a FP bias, not
an integer bias, and we probably think using XFmode for it makes it ok).

With -std=c99 and GCC 4.5 the code works as expected as we properly
truncate the XFmode result, that is -fexcess-precision=standard.

*** This bug has been marked as a duplicate of bug 323 ***