This question already has answers here:
Integer overflow concept
(2 answers)
Integer overflow and undefined behavior
(4 answers)
Closed 6 years ago.
While reading a book about programmign tricks I saw that -0x80000000 + -0x80000000 = 0. This didn't make sense to me so I wrote a quick C program below to test and indeed the answer is 0:
#include <stdio.h>
int main()
{
int x = -0x80000000;
int y = -0x80000000;
int z = x + y;
printf("Z is: %d", z);
return 0;
}
Could anyone shed any light as to why? I saw something about an overflow, but I can't see how an overflow causes 0 rather than an exception or other error. I get no warning or anything.
What's happening here is signed integer overflow, which is undefined behavior because the exact representation of signed integers is not defined.
In practice however, most machine use 2's complement representation for signed integers, and this particular program exploits that.
0x80000000 is an unsigned integer constant. The - negates it, changing the expression to signed. Assuming int is 32-bit on your system, this value still fits. In fact, it is the smallest value a signed 32-bit int can hold, and the hexadecimal representation of this number happens to be 0x80000000.
When adding numbers in 2's complement representation, it has the feature that you don't need to worry about the sign. They are added exactly the same way as unsigned numbers.
So when we add x and y, we get this:
0x80000000
+ 0x80000000
-------------
0x100000000
Because an int on your system is 32-bit, only the lowest 32 bits are kept. And the value of those bits is 0.
Again note that this is actually undefined behavior. It works because your machine uses 2's complement representation for signed integers and int is 32-bit. This is common for most machines / compilers, but not all.
What you're seeing is a lot of implementation defined behavior, very likely triggering undefined behavior at runtime. More than that is not possible to know without details about your and book writers architecture.
The result isn't meaningful without additional information. If you want a definite answer, consult the type ranges for your architecture and make sure the results of assignments and arithmetic fit into their respective types.
Related
I was working with integers in C, trying to explore more on when and how overflow happens.
I noticed that when I added two positive numbers, the sum of which overflows, I always got a negative number.
On the other hand, if I added two negative numbers, the sum of which overflows, I always got a positive number (including 0).
I made few experiments, but I would like to know if this is true for every case.
Integer overflows are undefined behavior in C.
C says an expression involving integers overflows, if its result after the usual arithmetic conversions is of a signed typed and cannot be represented in the type of the result. Assignment and cast expressions are an exception as they are ruled by the integer conversions.
Expressions of unsigned type cannot overflow, they wrap, e. g., 0U - 1 is UINT_MAX.
Examples:
INT_MAX + 1 // integer overflow
UINT_MAX + 1 // no overflow, the resulting type is unsigned
(unsigned char) INT_MAX // no overflow, integer conversion occurs
Never let any integer expression overflows, modern compilers (like gcc) take advantage of integer overflows being undefined behavior to perform various types of optimizations.
For example:
a - 10 < 20
when a is of type int after promotion, the expression is reduced in gcc (when optimization are enabled) to:
a < 30
It takes advantage of the expression being undefined behavior when a is in the range INT_MIN + 10 - 1 to INT_MIN.
This optimization could not be done when a is unsigned int because if a is 0, then a - 10 has to be evaluated as UINT_MAX - 9 (no undefined behavior). Optimizing a - 10 < 20 to a < 30 would then lead to a different result than the required one when a is 0 to 9.
Overflow of signed integers is undefined behaviour in C, so there are no guarantees.
That said, wrap around, or arithmetic modulo 2N, where N is the number of bits in the type, is a common behaviour. For that behaviour, indeed if a sum overflows, the result has the opposite sign of the operands.
Formally, the behaviour of signed arithmetic on overflow is undefined; anything can happen and it is 'correct'. This contrasts with unsigned arithmetic, where overflow is completely defined.
In practice, many older compilers used signed arithmetic which overflowed as you describe. However, modern GCC is making changes to the way it works, and you'd be very ill-advised to rely on the behaviour. It may change at any time when anything in the environment where your code is compiled changes — the compiler, the platform, ...
Overflow in C is a godawful mess.
Overflow during unsigned arithmetic or conversion to an unsigned type results in wraping modulo 2n
Overflow during conversion to a signed type is implementation defined, most implementations will wrap modulo 2n but some may not.
Overflow during signed arithmetic is undefined behaviour, according to the standard anything might happen. In practice sometimes it will do what you wan't, sometimes it will cause strange issues later in yoir code as the compiler optimises out important tests.
What makes things even worse is how this interacts with integer promotion. Thanks to promotion you can be doing signed arithmetic when it looks like you are doing unsigned arithmetic. For example consider the following code
uint16_t a = 65535;
uint16_t b = a * a;
On a system with 16-bit int this code is well-defined. However on a system with 32-bit int the multiplication will take place as signed int and the resulting overflow will be undefined behavior!
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
#include<stdio.h>
int main()
{
printf("%x", -1<<1);
getchar();
return 0;
}
Output:
Output is dependent on the compiler. For 32 bit compiler it would be fffffffe and for 16 bit it would be fffe.
This is from geek for geeks
-1 is a signed integer. A left shift on a signed integer with a negative value has undefined behavior according to the formal definition of the C language. This is part of the general rule that signed operations have undefined behavior when they overflow. the sign bit is set, and it must shift, but there's no room for it to go anywhere, so it overflows.
In practice, almost all platforms use two's complement representation for signed integers, and a left shift on a signed integer is treated as if the memory contained an unsigned integer. However, beware that compilers sometimes take advantage of the fact that this is undefined behavior to optimize in surprising ways.
-1 is all-bits-one, so a left shift drops the topmost bit and adds a 0 bit to the bottom. The result is 111…1110 in binary. If unsigned int is a 16-bit type, that's fffe in hexadecimal. If unsigned int is a 32-bit type, that's fffffffe. When that memory is read as a signed int, the value is -2 either way.
The %x specified requires an unsigned int as an argument. Passing an the signed version of the type is ok: it is converted to the unsigned value. The result of the conversion is 2^N - 2 where N is the number of bits in an unsigned int: as above, that's 0xfffe if N=16, 0xfffffffe if N=32.
It seems to me this answer is is just your 32-bit compiler casting (implicitly) -1 as long int for a signed integer, while the 16-bit compiler casts 1 to a "good old" int 16 bit integer.
Bad answer. A much better answer is given above by Gilles 'SO- stop being evil'. I am just editing this so it is not absolute non-sense.
As commented above, -1 is a signed integer, and left shifting (see here) a signed integer that is negative causes an undefined behavior because the sign bit has nowhere to go. In all the systems I have worked, that behavior is an overflow.
The reason why the results are different between 16-bit and 32-bit compilers might well just be the fact that 16-bit compilers use 16-bit integers, hence the 16 bit result fffe. I attached some code for you to try it out it find it would be useful.
Attached dirty test.
/* dirty_test.c
* This program left bit-shifts a signed integer and
* prints the byte content of the result to stdout.
*
* On *nix like systems, compile with
*
* cc dirty_test.c -o dirty_test.x
*/
#include <stdio.h>
int main(void)
{
int a;
// assigning a negative value causes the bit shift
// to produce an overflow.
a = -1;
printf("bytes before shift: %08x\n", a);
printf("bytes after shift: %08x\n", a << 1);
return 0;
}
I was working with integers in C, trying to explore more on when and how overflow happens.
I noticed that when I added two positive numbers, the sum of which overflows, I always got a negative number.
On the other hand, if I added two negative numbers, the sum of which overflows, I always got a positive number (including 0).
I made few experiments, but I would like to know if this is true for every case.
Integer overflows are undefined behavior in C.
C says an expression involving integers overflows, if its result after the usual arithmetic conversions is of a signed typed and cannot be represented in the type of the result. Assignment and cast expressions are an exception as they are ruled by the integer conversions.
Expressions of unsigned type cannot overflow, they wrap, e. g., 0U - 1 is UINT_MAX.
Examples:
INT_MAX + 1 // integer overflow
UINT_MAX + 1 // no overflow, the resulting type is unsigned
(unsigned char) INT_MAX // no overflow, integer conversion occurs
Never let any integer expression overflows, modern compilers (like gcc) take advantage of integer overflows being undefined behavior to perform various types of optimizations.
For example:
a - 10 < 20
when a is of type int after promotion, the expression is reduced in gcc (when optimization are enabled) to:
a < 30
It takes advantage of the expression being undefined behavior when a is in the range INT_MIN + 10 - 1 to INT_MIN.
This optimization could not be done when a is unsigned int because if a is 0, then a - 10 has to be evaluated as UINT_MAX - 9 (no undefined behavior). Optimizing a - 10 < 20 to a < 30 would then lead to a different result than the required one when a is 0 to 9.
Overflow of signed integers is undefined behaviour in C, so there are no guarantees.
That said, wrap around, or arithmetic modulo 2N, where N is the number of bits in the type, is a common behaviour. For that behaviour, indeed if a sum overflows, the result has the opposite sign of the operands.
Formally, the behaviour of signed arithmetic on overflow is undefined; anything can happen and it is 'correct'. This contrasts with unsigned arithmetic, where overflow is completely defined.
In practice, many older compilers used signed arithmetic which overflowed as you describe. However, modern GCC is making changes to the way it works, and you'd be very ill-advised to rely on the behaviour. It may change at any time when anything in the environment where your code is compiled changes — the compiler, the platform, ...
Overflow in C is a godawful mess.
Overflow during unsigned arithmetic or conversion to an unsigned type results in wraping modulo 2n
Overflow during conversion to a signed type is implementation defined, most implementations will wrap modulo 2n but some may not.
Overflow during signed arithmetic is undefined behaviour, according to the standard anything might happen. In practice sometimes it will do what you wan't, sometimes it will cause strange issues later in yoir code as the compiler optimises out important tests.
What makes things even worse is how this interacts with integer promotion. Thanks to promotion you can be doing signed arithmetic when it looks like you are doing unsigned arithmetic. For example consider the following code
uint16_t a = 65535;
uint16_t b = a * a;
On a system with 16-bit int this code is well-defined. However on a system with 32-bit int the multiplication will take place as signed int and the resulting overflow will be undefined behavior!
I am learning in C and I got a question regarding this conversion.
short int x = -0x52ea;
printf ( "%x", x );
output:
ffffad16
I would like to know how this conversion works because it's supposed to be on a test and we won't be able to use any compilers. Thank you
I would like to know how this conversion works
It is undefined behavior (UB)
short int x = -0x52ea;
0x52ea is a hexadecimal constant. It has the value of 52EA16, or 21,22610. It has type int as it fits in an int, even if int was 16 bit. OP's int is evidently 32-bit.
- negates the value to -21,226.
The value is assigned to a short int which can encode -21,226, so no special issues with assigning this int to a short int.
printf("%x", x );
short int x is passed to a ... function, so goes through the default argument
promotions and becomes an int. So an int with the value -21,226 is passed.
"%x" used with printf(), expects an unsigned argument. Since the type passed is not an unsigned (and not an int with a non-negative value - See exception C11dr §6.5.2.2 6), the result is undefined behavior (UB). Apparently the UB on your machine was to print the hex pattern of a 32-bit 2's complement of -21,226 or FFFFAD16.
If the exam result is anything but UB, just smile and nod and realize the curriculum needs updating.
The point here is that when a number is negative, it's structured in a completely different way.
1 in 16-bit hexadecimal is 0001, -1 is ffff. The most relevant bit (8000) indicates that it's a negative number (admitting it's a signed integer), and that's why it can only go as positive as 32767 (7fff), and as negative as -32768 (8000).
Basically to transform from positive to negative, you invert all bits and sum 1. 0001 inverted is fffe, +1 = ffff.
This is a convention called Two's complement and it's used because it's quite trivial to do arithmetic using bitwise operations when you use it.
Say I want to print some values. I am assuming that I should get Integer Overflow if my signed variable exceeds from TMin and TMax (in this case, using 4 byte int, 0x7FFFFFFF as Tmax and 0x80000000 as Tmin) but in these example I am not getting what I expect (explained in comments):
// Tmax = 0x7FFFFFFF == 2147483647
// Tmin = 0x80000000 == -2147483648
printf("1- overflow test: %d\n", 0x7FFFFFFF+1 ); // overflow - how compiler finds out it's a signed value, not an Unsigned one
printf("2- overflow test: %d\n", 0x80000000-1 ); // in contrast, why I am not getting an integer overflow here
printf("3- overflow test: %d\n", (int) 0x80000000-1 ); // overflow (Expected)
printf("4- overflow test: %d\n",(int) (0x7FFFFFFF+1)); // overflow (Expected)
First of all, let me tell you, (signed) integer overflow invokes undefined behavior.
In that scenario, anything can happen. You can neither trust nor reasonify the output of a code having UB.
Just to clarify, even
printf("2- overflow test: %d\n", 0x80000000-1 );
is UB. Though 0x80000000-1 is unsigned and not an overflow in itself, using %d will lead to a type mismatch which will technically lead to UB.
Regarding the Undefined behavior, from C11, annex §J.2,
Conversion to or from an integer type produces a value outside the range that can be
represented.
OP is not always experiencing signed integer overflow - which is undefined behavior.
The following is unsigned math as 0x80000000 is likely an unsigned integer. Hexadecimal constants are of the type that first fits them int, unsigned, long, unsigned long, ...
printf("2- overflow test: %d\n", 0x80000000-1 );
0x80000000-1 is an unsigned type as 0x80000000 first fits in an unsigned type, likely unsigned with the value of 2147483648u. 2147483648u - 1 --> 2147483647u.
0x7FFFFFFF+1 is a signed type as 0x7FFFFFFF first fits in a signed type, likely int with the value of INT_MAX.
int + int --> int and INT_MAX + 1 --> overflow.
OP said "0x80000000 as Tmin" is certainly a mis-understanding. In C, with 32-bit int/unsigned, 0x80000000 is a hexadecimal constant with the value of 2147483648. For OP, Tmin is more likely -INT_MAX - 1.
Trying to invoke integer overflow is undefined behavior. As per standard, in that case anything, including outwardly appearing non-overflow can happen. What this outputs is uninteresting and irrelevant.
It may be that your compiler optimizes it out. It may be that your system refuses to be used in such a way, it may be that a sack of rice falling in china caused a butterfly effect that lead to this. Its anything but defined what happens there.
It may also be that your system decided that its integers are bigger. Only the lower limit of integer sizes (2 byte) is mandated in c (at least 4 is typical for standard computers). Your system could have 8 byte integers, or even bigger ones.
C is kind of inconsistent about numeric exceptions.
There are a few things you can do that almost always cause problems. If you divide by 0, your program will usually crash pretty hard, just as if you'd accessed an invalid pointer.
There are a few things you can do that are guaranteed not to cause a problem. If you say
unsigned int i = UINT_MAX;
and then add 1 to it, it's guaranteed to wrap around to 0.
And then there are a number of things where the behavior is undefined or unspecified. Signed integer overflow is one of these. Strictly speaking it's undefined (anything can happen, you can't depend on it). In practice, most computers quietly wrap around, just like for unsigned arithmetic.
Now, everything I've said has been about the run-time behavior of a program, but in this question's posted code fragment, all the arithmetic happens at compile time. Compile-time arithmetic operates mostly according to the same rules as run-time, but not always. Modern compilers tend to warn you about problematic compile-time arithmetic (my copy of gcc emits three warnings for the posted fragment), but not always.