Understanding numerical overflow in C++ - c++

I am trying to understand better how overflows behaves in C++. Consider the following MWE (must check for integer literals):
#include <cstdint>
#include <iostream>
#include <iomanip>
int main() {
uint64_t known = 6049417284; // Known solution to operation.
uint32_t option_1 = 77778u; // Using 32 bits for operands.
uint64_t option_2 = 77778ull; // using 64 bits for operands.
uint64_t sol_option_1 = option_1*option_1;
uint64_t sol_option_2 = option_2*option_2;
std::cout << std::boolalpha << (sol_option_1 == known) << std::endl;
std::cout << (sol_option_2 == known) << std::endl;
}
Execution:
false
true
Why does it overflow with operands using 32 bits, even tough I explicitly request 64 bits to receive the solution?
My impression is that during run-time, C++ creates a temporary rvalue, whose precision is that from the operands, i.e. 32 bits. This overflows, and this result from an overflow is copied to the sol_option_1 variable, which receives the result from an overflow.

The answer is very simple: C++ doesn't care about what the result is being assigned to, it just takes the expression option_1 * option_1 and evaluates that. Then, it performs the assignment with the result. Note that you can also choose not to assign the result of an expression anywhere but that shouldn't affect the expression's evaluation.

The first argument determines the type of the result.
In the first line, it is option1 (32 bit).
In the 2nd line, it is option2 (64 bit).
You can solve the overflow of the first line with:
uint64_t sol_option_1 = (uint64_t) option_1*option_1;

Related

long long int bit representation, C++ [duplicate]

I want to use the following code in my program but gcc won't allow me to left shift my 1 beyond 31.
sizeof(long int) displays 8, so doesn't that mean I can left shift till 63?
#include <iostream>
using namespace std;
int main(){
long int x;
x=(~0 & ~(1<<63));
cout<<x<<endl;
return 0;
}
The compiling outputs the following warning:
left shift `count >= width` of type [enabled by default] `x=(~0 & ~(1<<63))`;
^
and the output is -1. Had I left shifted 31 bits I get 2147483647 as expected of int.
I am expecting all bits except the MSB to be turned on thus displaying the maximum value the datatype can hold.
Although your x is of type long int, the 1 is not. 1 is an int, so 1<<63 is indeed undefined.
Try (static_cast<long int>(1) << 63), or 1L << 63 as suggested by Wojtek.
You can't use 1 (int by default) to shift it beyond the int boundaries.
There's an easier way to get the "all bits except the MSB turned on" for a specific datatype
#include <iostream>
#include <limits>
using namespace std;
int main(){
unsigned long int max = std::numeric_limits<unsigned long int>::max();
unsigned long int max_without_MSB = max >> 1;
cout<< max_without_MSB <<endl;
return 0;
}
note the unsigned type. Without numeric_limits:
#include <iostream>
using namespace std;
int main() {
long int max = -1;
unsigned long int max_without_MSB = ((unsigned long int)max) >> 1;
cout << max_without_MSB << endl;
return 0;
}
Your title is misleading; a long can shift beyond 31 bits if a long is indeed that big. However your code shifts 1, which is an int.
In C++, the type of an expression is determined by the expression itself. An expression XXXXX has the same type regardless; if you later go double foo = XXXXX; it doesn't mean XXXXX is a double - it means a conversion happens from whatever XXXXX was, to double.
If you want to left-shift a long, then do that explicitly, e.g. 1L << 32, or ((long)1) << 32. Note that the size of long varies between platforms, so if you don't want your code to break when run on a different system then you'll have to take further measures, such as using fixed-width types, or shifting by CHAR_BIT * sizeof(long) - 1.
There is another issue with your intended code: 1L << 63 causes undefined behaviour if long is 64-bit or less. This is because of signed integer overflow; left-shift is defined the same as repeated multiplication of two, so attempting to "shift into the sign bit" causes an overflow.
To fix this, use unsigned types where it is fine to shift into the MSB, e.g. 1ul << 63.
Technically there is another issue in that ~0 doesn't do what you want if you are not on a 2's complement system, but these days it's pretty safe to ignore that case.
Looking at your overall intention with long x = ~0 & ~(1 << 63). A shorter way to write this is:
long x = LONG_MAX;
which is defined by <climits>. If you wanted 64-bit on all platforms then
int64_t x = INT64_MAX;
NB. If you do not intend to work with negative values then use unsigned long x and uint64_t respectively.
First let me state a few things about the shift, which is the source of your problem:
There is no guarantee that long int is actually 64 bit wide.
The most generic way I can think of is using std::numeric_limits:
static_cast<long int>(1) << (std::numeric_limits<long int>::digits - 1);
Now you can even make that a constexpr templated function:
template <typename Integer>
constexpr Integer foo()
{
return static_cast<Integer>(1) << (std::numeric_limits<Integer>::digits - 1);
}
So replacing the shift with static_cast<long int>(1) << (std::numeric_limits<long int>::digits - 1) will fix your issue, however there is a far better way:
std::numeric_limits includes a bunch of useful stuff, including:
std::numeric_limits<T>::max(); // the maximum value T can hold
std::numeric_limits<T>::min(); // the minimum value T can hold
std::numeric_limits<T>::digits; // the number of binary digits
std::numeric_limits<T>::is_signed(); // well, do I have to explain? ;-)
See cppreference.com for a complete list. You should prefer the facilities provided by the standard library, because it will most likely have fewer mistakes and other developers immediately know it.
The default datatype for a numeric value in C is integer unless explicitly mentioned.
Here you have to type cast the 1 as long int which would otherwise be an int.

24-bit to 32-bit conversion in C++

I need to convert a 24-bit integer (2s compliment) to 32-bit integer in C++. I have found a solution here, which is given as
int interpret24bitAsInt32(unsigned char* byteArray)
{
return (
(byteArray[0] << 24)
| (byteArray[1] << 16)
| (byteArray[2] << 8)
) >> 8;
}
Though I found it is working, I have the following concern about the piece of code.
byteArray[0] is only 8-bits, and hence how the operations like byteArray[0] << 24 will be possible?
It will be possible if the compiler up-converts the byteArray to an integer and does the operation. This may be the reason it is working now. But my question is whether this behaviour is guaranteed in all compilers and explicitly mentioned in the standard? It is not trivial to me as we are not explicitly giving the compiler any clue that the target is a 32-bit integer!
Also, please let me know any improvisation like vectorization is possible to improve the speed (may be using C++11), as I need to convert huge amount of 24-bit data to 32-bit.
int32_t interpret24bitAsInt32(unsigned char* byteArray)
{
int32_t number =
(((int32_t)byteArray[0]) << 16)
| (((int32_t)byteArray[1]) << 8)
| byteArray[2];
if (number >= ((int32_t)1) << 23)
//return (uint32_t)number | 0xFF000000u;
return number - 16777216;
return number;
}
this function should do what you want without invoking undefined behavior by shifting a 1 into the sign bit of int.
The int32_t cast is only necessary if sizeof(int) < 4, otherwise the default integer promotion to int happens.
If someone does not like the if: It does not get translated to a conditional jump by the compiler (gcc 9.2): https://godbolt.org/z/JDnJM2
It leaves a cmovg.
[expr.shift]/1 The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand...
[conv.prom] 7.6 Integral promotions
1 A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (7.15) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.
So yes, the standard requires that an argument of a shift operator, that has the type unsigned char, be promoted to int before the evaluation.
That said, the technique in your code relies on int a) being 32 bits large, and b) using two's-complement to represent negative values. Neither of which is guaranteed by the standard, though it's common with modern systems.
A version without branch; but multiplication:
int32_t interpret24bitAsInt32(unsigned char* bytes) {
unsigned char msb = UINT8_C(0xFF) * (bytes[0] >> UINT8_C(7));
uint32_t number =
(msb << UINT32_C(24))
| (bytes[0] << UINT32_C(16)))
| (bytes[1] << UINT32_C(8)))
| bytes[2];
return number;
}
You need to test if omitting the branch really gives you a performance advantage, though!
Adapted from older code of me which did this for 10 bit numbers. Test before use!
Oh, and it still relies upon implementation defined behaviour with regards to the conversion uint32_t to int32_t. If you want to go down that rabbit hole, have fun but be warned.
Or, much more simple: Use the trick from mchs answer. And also use shifts instead of multiplication:
int32_t interpret24bitAsInt32(unsigned char* bytes) {
int32_t const number =
(bytes[0] << INT32_C(16))
| (bytes[1] << INT32_C(8))
| bytes[2];
int32_t const correction =
(bytes[0] >> UINT8_C(7)) << INT32_C(24);
return number - correction;
}
Test case
There is indeed Integral_promotion for type smaller than int for operator_arithmetic
So assuming sizeof(char) < sizeof(int)
in
byteArray[0] << 24
byteArray is promoted in int and you do bit-shift on int.
First issue is that int can only be 16 bits.
Second issue (before C++20), int is signed, and Bitwise shift can easily lead to implementation-defined or UB (And you have both for negative 24 bits numbers).
In C++20, behavior of Bitwise shift has been simplified (behavior defined) and the problematic UB has been removed too.
The leading 1 of negative number are kept in neg >> 8.
So before C++20, you have to do something like:
std::int32_t interpret24bitAsInt32(const unsigned char* byteArray)
{
const std::int32_t res =
(std::int32_t(byteArray[0]) << 16)
| (byteArray[1] << 8)
| byteArray[2];
const std::int32_t int24Max = (std::int32_t(1) << 24) - 1;
return res <= int24Max ?
res : // Positive 24 bit numbers
int24Max - res; // Negative number
}
Integral promotions [conv.prom] are performed on the operands of a shift expression [expr.shift]/1. In your case, that means that your values of type unsigned char will be converted to type int before << is applied [conv.prom]/1. Thus, the C++ standard guarantees that the operands be "up-converted".
However, the standard only guarantees that int has at least 16 Bit. There is also no guarantee that unsigned char has exactly 8 Bit (it may have more). Thus, it is not guaranteed that int is always large enough to represent the result of these left shifts. If int does not happen to be large enough, the resulting signed integer overflow will invoke undefined behavior [expr]/4. Chances are that int has 32 Bit on your target platform and, thus, everything works out in the end.
If you need to work with a guaranteed, fixed number of Bits, I would generally recommend to use fixed-width integer types, for example:
std::int32_t interpret24bitAsInt32(const std::uint8_t* byteArray)
{
return
static_cast<std::int32_t>(
(std::uint32_t(byteArray[0]) << 24) |
(std::uint32_t(byteArray[1]) << 16) |
(std::uint32_t(byteArray[2]) << 8)
) >> 8;
}
Note that right shift of a negative value is currently implementation-defined [expr.shift]/3. Thus, it is not strictly guaranteed that this code will end up performing sign extension on a negative number. However, your compiler is required to document what exactly right-shifting a negative integer does [defns.impl.defined] (i.e., you can go and make sure it does what you need). And I have never heard of a compiler that does not implement right shift of a negative value as an arithmetic shift in practice. Also, it looks like C++20 is going to mandate arithmetic shift behavior…

Looping through bytes of a long prints out the bytes twice on 64-bit systems [duplicate]

I want to use the following code in my program but gcc won't allow me to left shift my 1 beyond 31.
sizeof(long int) displays 8, so doesn't that mean I can left shift till 63?
#include <iostream>
using namespace std;
int main(){
long int x;
x=(~0 & ~(1<<63));
cout<<x<<endl;
return 0;
}
The compiling outputs the following warning:
left shift `count >= width` of type [enabled by default] `x=(~0 & ~(1<<63))`;
^
and the output is -1. Had I left shifted 31 bits I get 2147483647 as expected of int.
I am expecting all bits except the MSB to be turned on thus displaying the maximum value the datatype can hold.
Although your x is of type long int, the 1 is not. 1 is an int, so 1<<63 is indeed undefined.
Try (static_cast<long int>(1) << 63), or 1L << 63 as suggested by Wojtek.
You can't use 1 (int by default) to shift it beyond the int boundaries.
There's an easier way to get the "all bits except the MSB turned on" for a specific datatype
#include <iostream>
#include <limits>
using namespace std;
int main(){
unsigned long int max = std::numeric_limits<unsigned long int>::max();
unsigned long int max_without_MSB = max >> 1;
cout<< max_without_MSB <<endl;
return 0;
}
note the unsigned type. Without numeric_limits:
#include <iostream>
using namespace std;
int main() {
long int max = -1;
unsigned long int max_without_MSB = ((unsigned long int)max) >> 1;
cout << max_without_MSB << endl;
return 0;
}
Your title is misleading; a long can shift beyond 31 bits if a long is indeed that big. However your code shifts 1, which is an int.
In C++, the type of an expression is determined by the expression itself. An expression XXXXX has the same type regardless; if you later go double foo = XXXXX; it doesn't mean XXXXX is a double - it means a conversion happens from whatever XXXXX was, to double.
If you want to left-shift a long, then do that explicitly, e.g. 1L << 32, or ((long)1) << 32. Note that the size of long varies between platforms, so if you don't want your code to break when run on a different system then you'll have to take further measures, such as using fixed-width types, or shifting by CHAR_BIT * sizeof(long) - 1.
There is another issue with your intended code: 1L << 63 causes undefined behaviour if long is 64-bit or less. This is because of signed integer overflow; left-shift is defined the same as repeated multiplication of two, so attempting to "shift into the sign bit" causes an overflow.
To fix this, use unsigned types where it is fine to shift into the MSB, e.g. 1ul << 63.
Technically there is another issue in that ~0 doesn't do what you want if you are not on a 2's complement system, but these days it's pretty safe to ignore that case.
Looking at your overall intention with long x = ~0 & ~(1 << 63). A shorter way to write this is:
long x = LONG_MAX;
which is defined by <climits>. If you wanted 64-bit on all platforms then
int64_t x = INT64_MAX;
NB. If you do not intend to work with negative values then use unsigned long x and uint64_t respectively.
First let me state a few things about the shift, which is the source of your problem:
There is no guarantee that long int is actually 64 bit wide.
The most generic way I can think of is using std::numeric_limits:
static_cast<long int>(1) << (std::numeric_limits<long int>::digits - 1);
Now you can even make that a constexpr templated function:
template <typename Integer>
constexpr Integer foo()
{
return static_cast<Integer>(1) << (std::numeric_limits<Integer>::digits - 1);
}
So replacing the shift with static_cast<long int>(1) << (std::numeric_limits<long int>::digits - 1) will fix your issue, however there is a far better way:
std::numeric_limits includes a bunch of useful stuff, including:
std::numeric_limits<T>::max(); // the maximum value T can hold
std::numeric_limits<T>::min(); // the minimum value T can hold
std::numeric_limits<T>::digits; // the number of binary digits
std::numeric_limits<T>::is_signed(); // well, do I have to explain? ;-)
See cppreference.com for a complete list. You should prefer the facilities provided by the standard library, because it will most likely have fewer mistakes and other developers immediately know it.
The default datatype for a numeric value in C is integer unless explicitly mentioned.
Here you have to type cast the 1 as long int which would otherwise be an int.

Bit wise '&' with signed vs unsigned operand

I faced an interesting scenario in which I got different results depending on the right operand type, and I can't really understand the reason for it.
Here is the minimal code:
#include <iostream>
#include <cstdint>
int main()
{
uint16_t check = 0x8123U;
uint64_t new_check = (check & 0xFFFF) << 16;
std::cout << std::hex << new_check << std::endl;
new_check = (check & 0xFFFFU) << 16;
std::cout << std::hex << new_check << std::endl;
return 0;
}
I compiled this code with g++ (gcc version 4.5.2) on Linux 64bit: g++ -std=c++0x -Wall example.cpp -o example
The output was:
ffffffff81230000
81230000
I can't really understand the reason for the output in the first case.
Why at some point would any of the temporal calculation results be promoted to a signed 64bit value (int64_t) resulting in the sign extension?
I would accept a result of '0' in both cases if a 16bit value is shifted 16 bits left in the first place and then promoted to a 64bit value. I also do accept the second output if the compiler first promotes the check to uint64_t and then performs the other operations.
But how come & with 0xFFFF (int32_t) vs. 0xFFFFU (uint32_t) would result in those two different outputs?
That's indeed an interesting corner case. It only occurs here because you use uint16_t for the unsigned type when you architecture use 32 bits for ìnt
Here is a extract from Clause 5 Expressions from draft n4296 for C++14 (emphasize mine):
10 Many binary operators that expect operands of arithmetic or enumeration type cause conversions ...
This pattern is called the usual arithmetic conversions, which are defined as follows:
...(10.5.3) — Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the
rank of the type of the other operand, the operand with signed integer type shall be converted to
the type of the operand with unsigned integer type.
(10.5.4) — Otherwise, if the type of the operand with signed integer type can represent all of the values of
the type of the operand with unsigned integer type, the operand with unsigned integer type shall
be converted to the type of the operand with signed integer type.
You are in the 10.5.4 case:
uint16_t is only 16 bits while int is 32
int can represent all the values of uint16_t
So the uint16_t check = 0x8123U operand is converted to the signed 0x8123 and result of the bitwise & is still 0x8123.
But the shift (bitwise so it happens at the representation level) causes the result to be the intermediate unsigned 0x81230000 which converted to an int gives a negative value (technically it is implementation defined, but this conversion is a common usage)
5.8 Shift operators [expr.shift]...Otherwise, if E1 has a signed type and non-negative value, and E1×2E2 is representable
in the corresponding unsigned type of the result type, then that value, converted to the result type, is the
resulting value;...
and
4.7 Integral conversions [conv.integral]...
3 If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.
(beware this was true undefined behaviour in C++11...)
So you end with a conversion of the signed int 0x81230000 to an uint64_t which as expected gives 0xFFFFFFFF81230000, because
4.7 Integral conversions [conv.integral]...
2 If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source
integer (modulo 2n where n is the number of bits used to represent the unsigned type).
TL/DR: There is no undefined behaviour here, what causes the result is the conversion of signed 32 bits int to unsigned 64 bits int. The only part part that is undefined behaviour is a shift that would cause a sign overflow but all common implementations share this one and it is implementation defined in C++14 standard.
Of course, if you force the second operand to be unsigned everything is unsigned and you get evidently the correct 0x81230000 result.
[EDIT] As explained by MSalters, the result of the shift is only implementation defined since C++14, but was indeed undefined behaviour in C++11. The shift operator paragraph said:
...Otherwise, if E1 has a signed type and non-negative value, and E1×2E2 is representable
in the result type, then that is the resulting value; otherwise, the behavior is undefined.
Let's take a look at
uint64_t new_check = (check & 0xFFFF) << 16;
Here, 0xFFFF is a signed constant, so (check & 0xFFFF) gives us a signed integer by the rules of integer promotion.
In your case, with 32-bit int type, the MSbit for this integer after the left shift is 1, and so the extension to 64-bit unsigned will do a sign extension, filling the bits to the left with 1's. Interpreted as a two's complement representation that gives the same negative value.
In the second case, 0xFFFFU is unsigned, so we get unsigned integers and the left shift operator works as expected.
If your toolchain supports __PRETTY_FUNCTION__, a most-handy feature, you can quickly determine how the compiler perceives expression types:
#include <iostream>
#include <cstdint>
template<typename T>
void typecheck(T const& t)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << t << '\n';
}
int main()
{
uint16_t check = 0x8123U;
typecheck(0xFFFF);
typecheck(check & 0xFFFF);
typecheck((check & 0xFFFF) << 16);
typecheck(0xFFFFU);
typecheck(check & 0xFFFFU);
typecheck((check & 0xFFFFU) << 16);
return 0;
}
Output
void typecheck(const T &) [T = int]
65535
void typecheck(const T &) [T = int]
33059
void typecheck(const T &) [T = int]
-2128412672
void typecheck(const T &) [T = unsigned int]
65535
void typecheck(const T &) [T = unsigned int]
33059
void typecheck(const T &) [T = unsigned int]
2166554624
The first thing to realize is that binary operators like a&b for built-in types only work if both sides have the same type. (With user-defined types and overloads, anything goes). This might be realized via implicit conversions.
Now, in your case, there definitely is such a conversion, because there simply isn't a binary operator & that takes a type smaller than int. Both sides are converted to at least int size, but what exact types?
As it happens, on your GCC int is indeed 32 bits. This is important, because it means that all values of uint16_t can be represented as an int. There is no overflow.
Hence, check & 0xFFFF is a simple case. The right side is already an int, the left side promotes to int, so the result is int(0x8123). This is perfectly fine.
Now, the next operation is 0x8123 << 16. Remember, on your system int is 32 bits, and INT_MAX is 0x7FFF'FFFF. In the absence of overflow, 0x8123 << 16 would be 0x81230000, but that clearly is bigger than INT_MAX so there is in fact overflow.
Signed integer overflow in C++11 is Undefined Behavior. Literally any outcome is correct, including purple or no output at all. At least you got a numerical value, but GCC is known to outright eliminate code paths which unavoidably cause overflow.
[edit]
Newer GCC versions support C++14, where this particular form of overflow has become implementation-defined - see Serge's answer.
0xFFFF is a signed int. So after the & operation, we have a 32-bit signed value:
#include <stdint.h>
#include <type_traits>
uint64_t foo(uint16_t a) {
auto x = (a & 0xFFFF);
static_assert(std::is_same<int32_t, decltype(x)>::value, "not an int32_t")
static_assert(std::is_same<uint16_t, decltype(x)>::value, "not a uint16_t");
return x;
}
http://ideone.com/tEQmbP
Your original 16 bits are then left-shifted which results in 32-bit value with the high-bit set (0x80000000U) so it has a negative value. During the 64-bit conversion sign-extension occurs, populating the upper words with 1s.
This is the result of integer promotion. Before the & operation happens, if the operands are "smaller" than an int (for that architecture), compiler will promote both operands to int, because they both fit into a signed int:
This means that the first expression will be equivalent to (on a 32-bit architecture):
// check is uint16_t, but it fits into int32_t.
// the constant is signed, so it's sign-extended into an int
((int32_t)check & (int32_t)0xFFFFFFFF)
while the other one will have the second operand promoted to:
// check is uint16_t, but it fits into int32_t.
// the constant is unsigned, so the upper 16 bits are zero
((int32_t)check & (int32_t)0x0000FFFFU)
If you explicitly cast check to an unsigned int, then the result will be the same in both cases (unsigned * signed will result in unsigned):
((uint32_t)check & 0xFFFF) << 16
will be equal to:
((uint32_t)check & 0xFFFFU) << 16
Your platform has 32-bit int.
Your code is exactly equivalent to
#include <iostream>
#include <cstdint>
int main()
{
uint16_t check = 0x8123U;
auto a1 = (check & 0xFFFF) << 16
uint64_t new_check = a1;
std::cout << std::hex << new_check << std::endl;
auto a2 = (check & 0xFFFFU) << 16;
new_check = a2;
std::cout << std::hex << new_check << std::endl;
return 0;
}
What's the type of a1 and a2?
For a2, the result is promoted to unsigned int.
More interestingly, for a1 the result is promoted to int, and then it gets sign-extended as it's widened to uint64_t.
Here's a shorter demonstration, in decimal so that the difference between signed and unsigned types is apparent:
#include <iostream>
#include <cstdint>
int main()
{
uint16_t check = 0;
std::cout << check
<< " " << (int)(check + 0x80000000)
<< " " << (uint64_t)(int)(check + 0x80000000) << std::endl;
return 0;
}
On my system (also 32-bit int), I get
0 -2147483648 18446744071562067968
showing where the promotion and sign-extension happens.
The & operation has two operands. The first is an unsigned short, which will undergo the usual promotions to become an int. The second is a constant, in one case of type int, in the other case of type unsigned int. The result of the & is therefore int in one case, unsigned int in the other case. That value is shifted to the left, resulting either in an int with the sign bit set, or an unsigned int. Casting a negative int to uint64_t will give a large negative integer.
Of course you should always follow the rule: If you do something, and you don't understand the result, then don't do that!

How does one safely static_cast between unsigned int and int?

I have an 8-character string representing a hexadecimal number and I need to convert it to an int. This conversion has to preserve the bit pattern for strings "80000000" and higher, i.e., those numbers should come out negative. Unfortunately, the naive solution:
int hex_str_to_int(const string hexStr)
{
stringstream strm;
strm << hex << hexStr;
unsigned int val = 0;
strm >> val;
return static_cast<int>(val);
}
doesn't work for my compiler if val > MAX_INT (the returned value is 0). Changing the type of val to int also results in a 0 for the larger numbers. I've tried several different solutions from various answers here on SO and haven't been successful yet.
Here's what I do know:
I'm using HP's C++ compiler on OpenVMS (using, I believe, an Itanium processor).
sizeof(int) will be at least 4 on every architecture my code will run on.
Casting from a number > INT_MAX to int is implementation-defined. On my machine, it usually results in a 0 but interestingly casting from long to int results in INT_MAX when the value is too big.
This is surprisingly difficult to do correctly, or at least it has been for me. Does anyone know of a portable solution to this?
Update:
Changing static_cast to reinterpret_cast results in a compiler error. A comment prompted me to try a C-style cast: return (int)val in the code above, and it worked. On this machine. Will that still be safe on other architectures?
Quoting the C++03 standard, §4.7/3 (Integral Conversions):
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
Because the result is implementation-defined, by definition it is impossible for there to be a truly portable solution.
While there are ways to do this using casts and conversions, most rely on undefined behavior that happen to have well-defined behaviors on some machines / with some compilers. Instead of relying on undefined behavior, copy the data:
int signed_val;
std::memcpy (&signed_val, &val, sizeof(int));
return signed_val;
You can negate an unsigned twos-complement number by taking the complement and adding one. So let's do that for negatives:
if (val < 0x80000000) // positive values need no conversion
return val;
if (val == 0x80000000) // Complement-and-addition will overflow, so special case this
return -0x80000000; // aka INT_MIN
else
return -(int)(~val + 1);
This assumes that your ints are represented with 32-bit twos-complement representation (or have similar range). It does not rely on any undefined behavior related to signed integer overflow (note that the behavior of unsigned integer overflow is well-defined - although that should not happen here either!).
Note that if your ints are not 32-bit, things get more complex. You may need to use something like ~(~0U >> 1) instead of 0x80000000. Further, if your ints are no twos-complement, you may have overflow issues on certain values (for example, on a ones-complement machine, -0x80000000 cannot be represented in a 32-bit signed integer). However, non-twos-complement machines are very rare today, so this is unlikely to be a problem.
Here's another solution that worked for me:
if (val <= INT_MAX) {
return static_cast<int>(val);
}
else {
int ret = static_cast<int>(val & ~INT_MIN);
return ret | INT_MIN;
}
If I mask off the high bit, I avoid overflow when casting. I can then OR it back safely.
C++20 will have std::bit_cast that copies bits verbatim:
#include <bit>
#include <cassert>
#include <iostream>
int main()
{
int i = -42;
auto u = std::bit_cast<unsigned>(i);
// Prints 4294967254 on two's compliment platforms where int is 32 bits
std::cout << u << "\n";
auto roundtripped = std::bit_cast<int>(u);
assert(roundtripped == i);
std::cout << roundtripped << "\n"; // Prints -42
return 0;
}
cppreference shows an example of how one can implement their own bit_cast in terms of memcpy (under Notes).
While OpenVMS is not likely to gain C++20 support anytime soon, I hope this answer helps someone arriving at the same question via internet search.
unsigned int u = ~0U;
int s = *reinterpret_cast<int*>(&u); // -1
Сontrariwise:
int s = -1;
unsigned int u = *reinterpret_cast<unsigned int*>(&s); // all ones