Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sign changes when going from int to float and back

Consider the following code, which is an SSCCE of my actual problem:

#include <iostream>  int roundtrip(int x) {     return int(float(x)); }  int main() {     int a = 2147483583;     int b = 2147483584;     std::cout << a << " -> " << roundtrip(a) << '\n';     std::cout << b << " -> " << roundtrip(b) << '\n'; } 

The output on my computer (Xubuntu 12.04.3 LTS) is:

2147483583 -> 2147483520 2147483584 -> -2147483648 

Note how the positive number b ends up negative after the roundtrip. Is this behavior well-specified? I would have expected int-to-float round-tripping to at least preserve the sign correctly...

Hm, on ideone, the output is different:

2147483583 -> 2147483520 2147483584 -> 2147483647 

Did the g++ team fix a bug in the meantime, or are both outputs perfectly valid?

like image 800
fredoverflow Avatar asked Dec 08 '13 12:12

fredoverflow


People also ask

What happens when you convert a float to an int?

Convert a float to an int always results in a data loss. The trunc() function returns the integer part of a number. The floor() function returns the largest integer less than or equal to a number. The ceil() function returns the smallest integer greater than or equal to a number.

How do you tell the difference between an int and a float?

An integer (more commonly called an int) is a number without a decimal point. A float is a floating-point number, which means it is a number that has a decimal place.

Can you go from int to float?

To convert an integer data type to float you can wrap the integer with float64() or float32. Explanation: Firstly we declare a variable x of type int64 with a value of 5. Then we wrap x with float64(), which converts the integer 5 to float value of 5.00.

Why does using float instead of int give me different results when all of my inputs are integers?

The reason you get different output is you perform a different computation: division behaves differently for integers and floating point numbers, except when the numerator is a multiple of the denominator.


1 Answers

Your program is invoking undefined behavior because of an overflow in the conversion from floating-point to integer. What you see is only the usual symptom on x86 processors.

The float value nearest to 2147483584 is 231 exactly (the conversion from integer to floating-point usually rounds to the nearest, which can be up, and is up in this case. To be specific, the behavior when converting from integer to floating-point is implementation-defined, most implementations define rounding as being “according to the FPU rounding mode”, and the FPU's default rounding mode is to round to the nearest).

Then, while converting from the float representing 231 to int, an overflow occurs. This overflow is undefined behavior. Some processors raise an exception, others saturate. The IA-32 instruction cvttsd2si typically generated by compilers happens to always return INT_MIN in case of overflow, regardless of whether the float is positive or negative.

You should not rely on this behavior even if you know you are targeting an Intel processor: when targeting x86-64, compilers can emit, for the conversion from floating-point to integer, sequences of instructions that take advantage of the undefined behavior to return results other than what you might otherwise expect for the destination integer type.

like image 138
Pascal Cuoq Avatar answered Sep 23 '22 15:09

Pascal Cuoq