-1

I have a problem where some additional bits are being set in a number:

void print2_helper(int x) {
  int isodd = x%2 != 0;
  x /= 2;
  if (x)
    print2_helper(x);
  putchar('0' + isodd);
}
printf("-7 << 16: ");
print2_helper((-7 << 16));

printf("\n(-7 << 16) | 75: ");
print2_helper((-7 << 16) | 75);
puts("\n");

Output:

-7 << 16: 1110000000000000000
(-7 << 16) | 75: 1101111111110110101

Why doing simple |75 produces such a weird number full of ones?

psprint
  • 349
  • 1
  • 10

1 Answers1

1

(-7 << 16) | 75 yields a negative value and print2_helper doesn't work correctly on negative values as others pointed out.

x /= 2 is not equivalent to shift right on negative integers. Lets examine how it results when x=-3 (we are using 5-bit integer for brevity)

11101 = -3

-3 / 2 = -1

-1 is 11111 in binary representation which is not expected result (01110).

Using unsigned integer argument on print2_helper should fix problem. (Together with fixing the issue PSKocik already mentioned in comments, which could be done by adding u suffix to make it unsigned integer: (-7u << 16) | 75)

Iwa
  • 512
  • 1
  • 5
  • 18
  • *which could be done by adding u suffix to make it unsigned integer: (-7u << 16) | 75)* Huh? What do you think `-7u` evaluates to? (Hint: start with the [unary minus operator](https://port70.net/~nsz/c/c11/n1570.html#6.5.3.3p3)) – Andrew Henle Jan 31 '21 at 14:08
  • There is no integer promotion for an `unsigned integer` in this case because its unary operator. See integer promotion rules: http://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/c/language/integer_conversion_rank.html – Iwa Jan 31 '21 at 14:20
  • Integer promotion is irrelevant. ["An integer constant begins with a digit ..."](https://port70.net/~nsz/c/c11/n1570.html#6.4.4.1p2) There are no negative integer constants. `-7u` is not one token - it's the positive value `7u` negated by the unary minus operator - so it's a signed integer value. – Andrew Henle Jan 31 '21 at 14:28
  • Result type doesn't change if integer promotion is absent, As specified in your "hint" link: `... The integer promotions are performed on the operand, and the result has the promoted type.` So, it results in unsigned type. – Iwa Jan 31 '21 at 14:41
  • Also, see clang's AST: https://godbolt.org/z/b9ndzE if it were to result in signed integer type, it would have inserted implicit cast to unsigned before returning it. – Iwa Jan 31 '21 at 14:49
  • Please explain how you do a cast in assembler. – Andrew Henle Jan 31 '21 at 16:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228080/discussion-between-iwa-and-andrew-henle). – Iwa Jan 31 '21 at 16:19
  • You ***REALLY*** need to think about what you're trying to argue here: an **unsigned** integer with a **negative** value. – Andrew Henle Jan 31 '21 at 16:21
  • OP wants to apply left-shift operation on negative numbers which is UB on signed integers, so we cast it to unsigned and do it. Which is noop, and has exactly same bit pattern as signed one, regardless of that signed value is negative or not. – Iwa Jan 31 '21 at 16:29
  • You still don't get it - `-7u` is not a ***cast*** to anything. It's two tokens - the unary `-` operator applied to the `unsigned int` integer constant value `7u`. The result is `-7` which is a signed `int` value. Again - "An integer constant begins with a digit..." The `-` is not a digit, it's not part of the integer constant. – Andrew Henle Jan 31 '21 at 16:35
  • I didn't imply there is a cast, as you can see that in godbolt example that there is no cast. My wording in last comment is misleading, I should have said "make it unsigned" instead "cast". Also about having two tokens, I'm not arguing against it but I still don't get your point. – Iwa Jan 31 '21 at 16:49
  • `"the unary - operator applied to the unsigned int integer constant value 7u. The result is -7 which is a signed int value."` No, the result type is unsigned int. And the excerpt from standard you pointed proves that given that there is no integer promotion happening. I'm gonna stop here, come into discussion chat for further if you want. – Iwa Jan 31 '21 at 17:12
  • There's **NOTHING** to chat about: you're claiming a negative value is unsigned. That's risible at this point. Just how can the value `-7` be unsigned? [Do you ***really*** think that can be an unsigned value?](https://www.gnu.org/software/libc/manual/html_node/Range-of-Type.html): "Each unsigned integer type has one such macro, for the maximum value; **the minimum value is, of course, zero**." Explain how `-7` fits in the range of `0` to `UINT_MAX` inclusive. If you don't understand that, you have no business writing C code. – Andrew Henle Jan 31 '21 at 18:56
  • No, I'm **not** claiming that unsigned integer contains negative values in range... But it **can** hold representation of them in exactly same bit pattern as in signed storage, and it is up to programmer whether to interpret that value by using signed type or unsigned type. Did you read question? – Iwa Jan 31 '21 at 19:26
  • `-7u` in C is a ***signed*** integer value. I've explained why. Integer promotions have nothing to do with it. Bit patterns have nothing to do with it. The programmer's interpretation of the value `-7u` is irrelevant - the compiler views it as a signed value, and left shifting a signed value that's negative is undefined behavior. Which your answer has ignored. – Andrew Henle Jan 31 '21 at 19:36
  • I don't know if there's much option left to convince you, standard clearly states that it's unsigned. Check https://godbolt.org/z/doMxGW – Iwa Jan 31 '21 at 19:42
  • Here's how compiler view it: https://github.com/llvm/llvm-project/blob/03d249396d6b736447d529915e77dfeb84f2eeae/clang/lib/CodeGen/CGExprScalar.cpp#L2715 Only special case here is floating points. Otherwise result type is `E->getType()` where `E` is operand of unary minus. Period. – Iwa Jan 31 '21 at 20:42
  • You don't even realize that what the compiler does in response to undefined behavior is irrelevant. `-7u` is negative, and left shifting a negative value is undefined behavior. The compiler is free to do whatever it wants - and it doesn't matter. If you want to write code that devolves into undefined behavior, that's on you. My standards are higher than that. You argument is merely that, in the face of that undefined behavior, this one implementation appears to give what appears to be the correct result. Which again, doesn't matter. – Andrew Henle Jan 31 '21 at 21:57
  • This is not about what compiler does in case of left-shifting negative signed int, it is implementation of unary minus. That code is front-end emitting IR from AST and it is about correctness of language semantics, it precisely follows what standard says. Just saying, result type of unary minus doesn't vary from implementation to implementation. Please tell me what part of it makes it so hard to grasp. Integer promotion rule is applied: `The result of the unary - operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has...` – Iwa Feb 01 '21 at 09:13
  • `...the promoted type.` but it is no-op according to integer promotion rules: `The following may be used in an expression wherever an int or unsigned int may be used: -An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int. -A bit-field of type _Bool, int, signed int, or unsigned int..` None of them are our case, thus promoted type is same as operand type, so is result type. (NB `and the result has the promoted type.`) – Iwa Feb 01 '21 at 09:15
  • sorry @AndrewHenle but as Iwa pointed out with the provided documents, shifting -7u is not an undefined behavior since the compiler treats -7u as an unsigned type and also answers OP's question since he wants to shift bit pattern of -7 including sign bit. – Shine Feb 01 '21 at 12:34
  • @Shine *the compiler treats -7u as an unsigned type* Doesn't matter - the compiler treating a signed value as unsigned in a case of undefined behavior is totally irrelevant. Don't also fall into the trap of thinking how a compiler implements undefined behavior as meaningful in any way. Do ***you*** understand what undefined behavior really means? – Andrew Henle Feb 01 '21 at 12:36
  • *there is no **UB** here*? ORLY?: [**Why does left shift operation invoke Undefined Behaviour when the left side operand has negative value?**](https://stackoverflow.com/questions/3784996/why-does-left-shift-operation-invoke-undefined-behaviour-when-the-left-side-oper) – Andrew Henle Feb 01 '21 at 12:54
  • What part of `If negation operand is a type which is unsigned and has rank greater or equal to unsigned int, it results in unsigned type.` you did not understand? left-shift is applied on `unsigned int`. References from standard, compiler implementation, a C code asserting type is unsigned code is given above. – Iwa Feb 01 '21 at 13:03