6

I am using gcc to test some simple casts between float to unsigned int.

The following piece of code gives the result 0.

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) maxFloat;
printf("%u\n", a);

0 is printed (which I belive is very strange).

On the other hand the following piece of code:

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) (signed int) maxFloat;
printf("%u\n", a);

prints 2147483648 which I belive is the correct results.

What happens that I get 2 different results?

dbush
  • 205,898
  • 23
  • 218
  • 273
user3523954
  • 307
  • 1
  • 2
  • 12
  • 1
    "*which I belive is the correct results.*" <- I wonder why you believe this? It's clearly not the same number. –  May 02 '18 at 12:36
  • A cast will convert your value to a different type. The value will **not** change, as long as it can be represented in the new type. –  May 02 '18 at 12:37
  • I believe because of this explanation https://en.wikipedia.org/wiki/Single-precision_floating-point_format (see section Precision limits on integer values) – user3523954 May 02 '18 at 12:40
  • again, casts work on the values, not on the representations. –  May 02 '18 at 12:41
  • I can leave with loosing precision but getting 0 is totally wrong... or? – user3523954 May 02 '18 at 12:44
  • 1
    How can something be "totally wrong" if the specification doesn't say what should be correct? – StoryTeller - Unslander Monica May 02 '18 at 13:06
  • 2
    @StoryTeller: There is a specification that says what the result of representing 424967295 and printing it is: Mathematics. [user3523954](https://stackoverflow.com/users/3523954/user3523954)’s statement that they can live with losing precision but getting zero is wrong is a statement that they would like C to conform more closely to mathematics. For example, the result of converting an out-of-bounds floating-point value to integer could be saturation rather than undefined. Theirs was a fairly simple statement to understand, and your response comes across as mocking or abrasive. – Eric Postpischil May 02 '18 at 13:18
  • 1
    @EricPostpischil I strongly disagree. There was more than enough explanation on how C works given. If OP insists this is "wrong", I don't see any way to help further. –  May 02 '18 at 13:20
  • @EricPostpischil - Well, I'm sorry you feel this way. But the point remains that the **C specification** leaves it hanging, and for a good reason (perhaps less so now, but a very good one historically nonetheless). – StoryTeller - Unslander Monica May 02 '18 at 13:20
  • @FelixPalmen: It is not a dispute about how C works. [user3523954](https://stackoverflow.com/users/3523954/user3523954)’s comment does not say their C implementation is wrong (does not conform with the C standard). It is a statement they wished C worked differently. If you do not see a way to help further, then consider that C is Turing complete (aside from practical limitations on resources), so whatever algorithm OP desires could be implemented in some manner. Therefore there is a way to help them achieve the behavior they want. – Eric Postpischil May 02 '18 at 13:24
  • 1
    @EricPostpischil - This is where you are wrong. The OP's comment most definitely indicates they believe their implementation is doing something wrong. And as you yourself indicated, they have reason to believe it, because Mathematics disagrees with their observed result. – StoryTeller - Unslander Monica May 02 '18 at 13:25
  • @EricPostpischil the question was "*What happens that I get 2 different results?*", the answer to that is **undefined behavior** (with a lot more explanation given). OP is unwilling to accept that fact. –  May 02 '18 at 13:26
  • @StoryTeller: No, it does not say they believe **their implementation** is doing something wrong. No noun phase in their comment refers to their implementation. Their comment indicates they want a different result. It does not say the fault for that lack is in their implementation. – Eric Postpischil May 02 '18 at 13:26
  • @EricPostpischil just read the comments on my response. This discussion is pointless I guess ... –  May 02 '18 at 13:28
  • 1
    @EricPostpischil - I suppose we'll have to agree to disagree about divining intent here. Both the OP's and in my own comments. – StoryTeller - Unslander Monica May 02 '18 at 13:29
  • @FelixPalmen: OP has not indicated unwillingness to accept that the behavior is undefined. Rather, their comments show they do not actually understand the implications of behavior being undefined. Their comments suggest they think that if the C standard does not define the behavior, the behavior falls back to something else (which it does, of course, but they think that something else is more deterministic than it is). Nobody here has yet explained to them explicitly that undefined behavior can have a wide variety of results that easily differ when small changes in environment are made. – Eric Postpischil May 02 '18 at 13:30
  • @StoryTeller: I will not agree to disagree. If you cannot or will not support your position, that is your issue. Nonetheless, I recommend you work harder to comprehend other people’s thinking. When something looks cryptic, like a refusal to accept something, there is often a simple explanation when seen from another view. – Eric Postpischil May 02 '18 at 13:30
  • 1
    @EricPostpischil - But I will not agree with you. So we are at an impasse. Nothing left to do, I'm afraid. I'll forgive your own condescending tone this time. Have a good day. – StoryTeller - Unslander Monica May 02 '18 at 13:31
  • 1
    @EricPostpischil quoting OP: "*I understand the meaning of undefined*". Further quotes include "*I don't agree.*" and "*A computer and a compiler are finite state machines.*" (well, thanks a lot!). End of discussion for me, bye. –  May 02 '18 at 13:32
  • @StoryTeller: Yes, there are things left to do. You could ask questions of the OP instead of making abrupt statements. You could seek the gaps in their knowledge and the causes of their beliefs and then provide explanations. – Eric Postpischil May 02 '18 at 13:32
  • @FelixPalmen: The fact that somebody says they understand the meaning of a word does not mean they fully understand its implications. – Eric Postpischil May 02 '18 at 13:34

2 Answers2

6

If you first do this:

printf("%f\n", maxFloat);

The output you'll get is this:

4294967296.000000

Assuming a float is implemented as an IEEE754 single precision floating point type, the value 4294967295.0 cannot be represented exactly by this type because there's aren't enough bits of precision. The closest value it can store is 4294967296.0.

Assuming an int (and likewise unsigned int) is 32 bits, the value 4294967296.0 is outside the range of both of these types. Converting a floating point type to an integer type when the value cannot be represented in the given integer type invokes undefined behavior.

This is detailed in section 6.3.1.4 of the C standard which dictates conversion from floating point types to integer types:

1 When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.61)

...

61) The remaindering operation performed when a value of integer type is converted to unsigned type need not be performed when a value of real floating type is converted to unsigned type. Thus, the range of portable real floating values is (−1, Utype_MAX+1).

The footnote in the above passage is referencing section 6.3.1.3, which details integer to integer conversions:

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

The behavior you see in the first code snippet is consistent with an out-of-range conversion to an unsigned type when the value in question is an integer, however because the value being converted has a floating point type it is undefined behavior.

Just because one implementation does this doesn't mean that all will. In fact, gcc gives a different result if you change the optimization settings.

For example, on my machine using gcc 5.4.0, given this code:

float n = 4294967296;
printf("n=%f\n", n);
unsigned int a = (unsigned int) n;
int b = (signed int) n;
unsigned int c = (unsigned int) (signed int) n;
printf("a=%u\n", a);
printf("b=%d\n", b);
printf("c=%u\n", c);

I get the following results with -O0:

n=4294967296.000000
a=0
b=-2147483648
c=2147483648

And this with -O1:

n=4294967296.000000
a=4294967295
b=2147483647
c=2147483647

If on the other hand n is defined as long or long long, you would always get this output:

n=4294967296
a=0
b=0
c=0

The conversion to unsigned is well defined by the C standard as sited above, and the conversion to signed is implementation defined, which gcc defines as follows:

The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).

For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • I still get 0 even if a compile with -O1. Probably is because of the old gcc (4.3.4) that I have. – user3523954 May 02 '18 at 13:16
  • 2
    @user3523954 That's another example of undefined behavior. Because the standard imposes no requirements in this case, an implementation is free to do whatever it wants at any time and it doesn't have to document exactly what it's doing. – dbush May 02 '18 at 13:36
2

Assuming IEEE 754 floating point numbers, the number 4294967295.0 can't be stored exactly in a float. It will be stored as 4294967296.0 instead (which is 232).

Further assuming your unsigned int has 32 value bits, this is just by one too large to fit in an unsigned int, so the result of the conversion is undefined according to the C standard -- 0 is a "reasonable" outcome.

In your second case, you have undefined behavior as well, and I have no theory what's happening here on the representation level. Fact is, the number is much too large for a 32 bit signed int (still assuming this is what your machine uses).


From this remark in your question:

prints 2147483648 which I belive is the correct results.

I assume you wanted to see the representation of your float in memory. Casting will convert the value, so that's not the way to see the representation. The following code would do:

int main(void) {
    const float maxFloat = 4294967295.0;
    unsigned char *floatBytes = &maxFloat;
    for (int i=0; i < sizeof maxFloat; ++i)
    {
        printf("0x%02x ", floatBytes[i]);
    }
    puts("");
}

online example

  • I don't agree. The second piece of code does not show 0. – user3523954 May 02 '18 at 12:42
  • I believe the result of the conversion in this case is actually undefined. If I'm reading [6.3.1.4p1](http://port70.net/~nsz/c/c11/n1570.html#6.3.1.4p1) correctly. – StoryTeller - Unslander Monica May 02 '18 at 12:42
  • @StoryTeller that's correct -- I'm already assuming a lot of things here ;) I'll word it better –  May 02 '18 at 12:43
  • I know I know. But I think that note needs to be added to address the OP's confusion when casting to `signed int` first. It doesn't have to be the same value. – StoryTeller - Unslander Monica May 02 '18 at 12:44
  • So both pieces of code run into the undefined problem ... right? How it can be so different results?! I understand the meaning of undefined ,,, but still – user3523954 May 02 '18 at 12:47
  • how? well your compiler is doing something different when a converted float value overflows a **signed** integer type. Not much to wonder about. That's the nature of *undefined*. –  May 02 '18 at 12:47
  • Oh and .. you don't have to "agree" with my answers, thanks ;) Actually, I like [dbush's answer](https://stackoverflow.com/a/50134990/2371524) better, being more exact including a standard quote ... but it basically tells you the same thing. –  May 02 '18 at 12:51
  • and you don't have to take it personally. A computer and a compiler are finite state machines. Hard to believe that "undefined" has so different meaning in the 2 different situations ... May be put the blame on undefined to easily ... – user3523954 May 02 '18 at 12:58
  • so you don't see different "states" for different types to convert to? Then, well, just don't believe it. –  May 02 '18 at 12:59
  • 1
    @user3523954 *"Hard to believe that "undefined" has so different meaning in the 2 different situations"* - Believe it – StoryTeller - Unslander Monica May 02 '18 at 13:02