Reputation: 292
I have two variables (test1
and test2
), both unsigned.
I need to check which of them is bigger.
I'm trying to understand what happens if overflow occurs.
My first test was done with uint8_t (char) data type:
#include <stdio.h>
#include <stdint.h>
#include <math.h>
int main()
{
uint8_t test1 = 0;
printf("test1 = %d\n", test1);
uint8_t test2 = pow(2, 8 * sizeof(test1)) - 1; //max holdable value of uint8_t
printf("test2 = %d\n", test2);
uint8_t test3 = test1 - test2;
printf("test1 - test2 = %d\n", test3);
if ((test1 - test2) == 0)
printf("test1 == test2\n");
if ((test1 - test2) > 0)
printf("test1 > test2\n");
if ((test1 - test2) < 0)
printf("test1 < test2\n");
if (test3 == 0)
printf("test1 == test2\n");
if (test3 > 0)
printf("test1 > test2\n");
if (test3 < 0)
printf("test1 < test2\n");
return 0;
}
output:
test1 = 0
test2 = 255
test1 - test2 = 1
test1 < test2
test1 > test2
What? Making the substraction and save it in a variable, then checking it, is different from checking the substraction on the fly?
My second test was done with uint32_t (long) data type:
#include <stdio.h>
#include <stdint.h>
#include <math.h>
int main()
{
uint32_t test1 = 0;
printf("test1 = %d\n", test1);
uint32_t test2 = pow(2, 8 * sizeof(test1)) - 1; //max holdable value of uint32_t
printf("test2 = %lu\n", test2);
uint32_t test3 = test1 - test2;
printf("test1 - test2 = %d\n", test3);
if ((test1 - test2) == 0)
printf("test1 == test2\n");
if ((test1 - test2) > 0)
printf("test1 > test2\n");
if ((test1 - test2) < 0)
printf("test1 < test2\n");
if (test3 == 0)
printf("test1 == test2\n");
if (test3 > 0)
printf("test1 > test2\n");
if (test3 < 0)
printf("test1 < test2\n");
return 0;
}
output:
test1 = 0
test2 = 4294967295
test1 - test2 = 1
test1 > test2
test1 > test2
What??? Now making the substraction and saving it in a variable, then check it, is the same as checking the substraction on the fly?
SO I was expecting that substraction between unsigned values (without an explicit cast) ALWAYS returns a value >= 0. But doing the substraction inside the IF leads to unexpected results.
Now I'm confused. Can someone explain this behaviour to me?
Upvotes: 8
Views: 6402
Reputation: 30813
Because of type promotion, your uint8
will be promoted to platform-based int
, which is likely to be greater than 8-bit (usually 32-bit) in most of nowadays' Computer.
this line:
if ((test1 - test2) < 0)
if equivalent to
if ((int)((int)test1 - (int)test2) < 0) = -255 < 0
which is true.
However, for this line,
uint8_t test3 = test1 - test2;
it is equivalent to
uint8_t test3 = (uint8_t)((int)test1 - (int)test2); //(uint8_t)(-255) = 1!!;
So both test1 - test2 < 0
and test3 > 0
are (welcome to computer's world!) correct!
That explains your result.
But for uint32
, because it is of "higher" rank than native int
, so it is not "promoted" and stays as uint32
all the time, or in other words
this line:
if ((test1 - test2) > 0)
if equivalent to
if ((uint32_t)((uint32_t)test1 - (uint32_t)test2) > 0) = large positive number > 0
and this line
uint32_t test3 = test1 - test2;
is equivalent to
uint32_t test3 = (uint32_t)test1 - (uint32_t)test2; //also large positive number
thus you have both test1 - test2 > 0
and test3 > 0
.
So, in your two cases, both can only be correct if running on 8-bit machine. Or, suppose in the future, native int
is 64-bit, then both cases will be correct too...
Upvotes: 6
Reputation: 93566
The somewhat arcane type promotion rules apply. In the first example where the operands are uint8_t
, for the expression:
test1 - test2
both operands are implicitly promoted to int
before subtraction, and the expression itself has type int
.
Upvotes: 9