Bug 48236 - uint64_t and int64_t behave differently when converting to float
Summary: uint64_t and int64_t behave differently when converting to float
Status: RESOLVED DUPLICATE of bug 323
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 4.4.5
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-22 12:57 UTC by Jon Valdes
Modified: 2011-03-22 15:15 UTC (History)
1 user (show)

See Also:
Host:
Target: i?86-*-*
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
Small code to show uint64_t / int64_t difference (195 bytes, text/plain)
2011-03-22 12:57 UTC, Jon Valdes
Details

Note You need to log in before you can comment on or make changes to this bug.
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 ***