12

I was just taking a C++ test and I got the following question wrong:

Q: What is the output of the following program?

#include <iostream>
#include <stdint.h>

using namespace std;

int main() {
    int a = 0;
    for (int8_t i = 1; i > 0; i <<= 1)
        a++;
    cout << a;
    return 0;
}

There were the following answers to choose from

  • 8
  • 7
  • Undefined Behavior
  • Compile Error

The "correct" answer was 7. If there was "Implementation-Defined Behavior" in the answers, I would choose that, so I chose Undefined Behavior which was sort of the closest. I understand that in sign-and-magnitute, 1's complement, and 2's complement the answer will be 7. But doesn't the C++ standard theoretically allow any other number representations? For example, sign and magnitude, but 0 means negative?

Am I correct in that the real correct answer to this question should be Implementation-Defined Behavior, and if not, could you please explain why the answer is 7 regardless of the implementation?

I read the comments to the question and it appears that initially the type of a was char, which apparently had raised a lot of complaints about whether char is signed or not, so the testsetter changed it to int8_t. As a bonus question, is <stdint.h> part of C++? O_O

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • `` is part of C++ (included for compatibility with C). There's a C++ version of it, ``. – Alexey Frunze Mar 24 '13 at 10:14
  • Any sane C++ implementation should provide `stdint.h`, but in C++11 there is officially `cstdint`. (@AlexeyFrunze: C++11 only!) – John Zwinck Mar 24 '13 at 10:16
  • 2
    [Implementations don't even have to provide `int8_t`](http://stackoverflow.com/a/5254321/168175) – Flexo Mar 24 '13 at 10:16
  • @Flexo: yeah, it seemed odd to me that `int8_t` was used to clarify that a `char` was signed - they could have just used `signed char`. – John Zwinck Mar 24 '13 at 10:18
  • 4
    @JohnZwinck: The problem with signed char is that it doesn't have to have 8 bits :) – Armen Tsirunyan Mar 24 '13 at 10:19
  • Unsigned values represented in a signed integer type are required to have the same *value representation* as the same value in the corresponding unsigned integer type. So I don't think sign and magnitude where a `0` sign means negative would be allowed. (On the other hand, I'm pretty sure the `<<=` is evil when applied to signed datatypes) – jalf Mar 24 '13 at 10:22
  • 4
    The test is wrong. The correct answer is UB. – Alexey Frunze Mar 24 '13 at 10:29
  • What a strange "test". What knowledge, exactly, is this testing? If you have to *wonder* whether the code is UB or not, no sane programmer would ever write it. – Cody Gray - on strike Mar 24 '13 at 10:45
  • 1
    Another possibility is that it won't compile, because `int8_t` is not defined. That would be unusual, but on a system that doesn't have an 8-bit integral type, `int8_t` won't exist. – Pete Becker Mar 24 '13 at 12:39
  • Related: [Why does left shift operation invoke Undefined Behaviour when the left side operand has negative value?](http://stackoverflow.com/questions/3784996/why-does-left-shift-operation-invoke-undefined-behaviour-when-the-left-side-oper) – Alvin Wong Mar 24 '13 at 14:12

3 Answers3

18

I would say it is undefined (and not implementation-defined) for a different reason.

From 5.8:3

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 × 2E2 , reduced modulo one more than the maximum value representable in the result type. 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.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
jalf
  • 243,077
  • 51
  • 345
  • 550
  • I reckon that makes both UB and compiler error correct then since they don't specify which compiler it's using. – Flexo Mar 24 '13 at 10:32
  • 2
    No, it is undefined behavior and nothing else. Of course, the compiler is free to deal with UB in any way it likes (including producing a nice, simple compile error), but if the question is what ISO C++ says the result of that program should be, then the only correct answer is UB. :) – jalf Mar 24 '13 at 11:02
  • I think you're missing a `^` character in the first `E1 x 2 E2`. – David G Mar 24 '13 at 14:03
  • So his program has undefined behavior because he has signed overflow (because the 1 rolls off the bit pattern when shifting left)? – David G Mar 24 '13 at 14:18
7

If the implementation provides the optional int8_t, the answer should be correct, from the C99 draft which C++11 references regarding stdint;

7.18.1.1 Exact-width integer types

The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two’s complement representation. Thus, int8_t denotes a signed integer type with a width of exactly 8 bits.

Community
  • 1
  • 1
Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • 2
    Well, this doesn't address the other important fact, what happens when you shift 1 into the sign bit. And that is UB, actually. – Alexey Frunze Mar 24 '13 at 10:27
  • 2
    This is interesting though. Does this only apply to the `intN_t` types? Or does C11 also require "normal" integer types to be two's complement? – jalf Mar 24 '13 at 10:30
  • By the way, I'm pretty sure C++11 doesn't reference C11. Wouldn't it reference C99 rather? – jalf Mar 24 '13 at 10:32
  • @jalf Yep, it says so (mentions C99) explicitly at the very beginning. – Alexey Frunze Mar 24 '13 at 10:34
3

Since the immediate question was answered, I'll answer the bonus question: <stdint.h> is a C header which is available in C++. However, use the modern C++ header <cstdint> instead.

David G
  • 94,763
  • 41
  • 167
  • 253