I was trying to print the minimum of int, char, short, long
without using the header file <limit.h>
. So bitwise operation will be a good choice. But something strange happened.
The statement
printf("The minimum of short: %d\n", ~(((unsigned short)~0) >> 1));
gives me
The minimum of short: -32768
But the statement
printf("The minimum of short: %d\n", ~((~(unsigned short)0) >> 1));
gives me
The minimum of short: 0
This phenomenon also occurs in char
. But it does not occur in long, int
. Why does this happen?
And it is worth mentioning that I use VS Code as my editor. When I moved my cursor on unsigned char
in the statement
printf("The minimum of char: %d\n", (short)~((~(unsigned char)0) >> 1));
It gives me a hint (int) 0
instead of (unsigned char)0
, which I expected. Why does this happen?
It depends on how many logical statements there are. When there is only one logical statement, it's always going to evaluate the same way, because it's just a single statement. When there are two logical statements, order does not matter, because we can only apply one logical statement & (and) or | (or) between them.
The bitwise AND may be used to clear selected bits (or flags) of a register in which each bit represents an individual Boolean state. This technique is an efficient way to store a number of Boolean values using as little memory as possible. Because 6 AND 1 is zero, 6 is divisible by two and therefore even.
The & (bitwise AND) in C or C++ takes two numbers as operands and does AND on every bit of two numbers. The result of AND is 1 only if both bits are 1. The | (bitwise OR) in C or C++ takes two numbers as operands and does OR on every bit of two numbers. The result of OR is 1 if any of the two bits is 1.
There are four bitwise operators in IDL: AND, NOT, OR, and XOR. For integer operands (byte, signed- and unsigned-integer, longword, and 64-bit longword data types), bitwise operators operate on each bit of the operand or operands independently.
First of all, none of your code is really reliable and won't do what you expect.
printf
and all other variable argument length functions have a dysfunctional "feature" called the default argument promotions. This means that the actual type of the parameters passed undergo silent promotion. Small integer types (such as char
and short
) get promoted to int
which is signed. (And float gets promoted to double.) Tl;dr: printf
is a nuts function.
Therefore you can cast between various small integer types all you want, there will still be a promotion to int
in the end. This is no problem if you use the correct format specifier for the intended type, but you don't, you use %d
which is for int
.
In addition, the ~
operator, like most operators in C, performs implicit integer promotion of its operand. See Implicit type promotion rules.
That being said, this line ~((~(unsigned short)0) >> 1)
does the following:
Take the literal 0
which is of type int
and convert to unsigned short
.
Implicitly promote that unsigned short
back to int
through implicit integer promotion.
Calculate the bitwise complement of the int
value 0
. This is 0xFF...FF
hex, -1
dec, assuming 2's complement.
Right shift this int
by 1. Here you invoke implementation-defined behavior upon shifting a negative integer. C allows this to either result in a logical shift = shift in zeroes, or arithmetic shift = shift in a sign bit. Different result from compiler to compiler and non-portable.
You get either 0x7F...FF
in case of logical shift or 0xFF...FF
in case of arithmetic shift. In this case it seems to be the latter, meaning you still have decimal -1
after shift.
You do bitwise complement of the 0xFF...FF
= -1
and get 0
.
You cast this to short
. Still 0
.
Default argument promotion convert it to int
. Still 0
.
%d
expects a int
and therefore prints accordingly. unsigned short
is printed with %hu
and short
with %hd
. Using the correct format specifier should undo the effect of default argument promotion.
Advice: study implicit type promotion and avoid using bitwise operators on operands that have signed type.
To simply display the lowest 2's complement value of various signed types, you have to do some trickery with unsigned types, since bitwise operations on their signed version are unreliable. Example:
int shift = sizeof(short)*8 - 1; // 15 bits on sane systems
short s = (short) (1u << shift);
printf("%hd\n", s);
This shifts an unsigned int 1u
15 bits, then converts the result of that to short, in some "implementation-defined way", meaning on two's complement systems you'll end up converting 0x8000 to -32768.
Then give printf
the right format specifier and you'll get the expected result from there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With