Why does int8_t and user input via cin shows strange result [duplicate]
Asked Answered
T

5

3

A tiny piece of code drives me crazy but hopefully you can prevent me from jumping out of the window. Look here:

#include <iostream>
#include <cstdint>

int main()
{
    int8_t i = 65;
    int8_t j;

    std::cout << "i = " << i << std::endl; // the 'A' is ok, same as uchar

    std::cout << "Now type in a value for j (use 65 again): " << std::endl;
    std::cin >> j;
    std::cout << "j = " << j << std::endl;

    if (i != j) 
        std::cout << "What is going on here?????" << std::endl;
    else 
        std::cout << "Everything ok." << std::endl;

    return 0;
}

If I use int instead of int8_t everything ok. I need this as 8-bit unsigned integers, not bigger. And btw. with unsigned char it's the same behaviour - of course - as with int8_t.

Anyone with a hint?

Tundra answered 7/7, 2014 at 18:59 Comment(1)
int8_t can be any 8-bit signed integer type. char is perfectly valid.Heinrike
A
10

int8_t is a typedef for an integer type with the required characteristics: pure 2's-complement representation, no padding bits, size of exactly 8 bits.

For most (perhaps all) compilers, that means it's going to be a typedef for signed char.(Because of a quirk in the definition of the term signed integer type, it cannot be a typedef for plain char, even if char happens to be signed).

The >> operator treats character types specially. Reading a character reads a single input character, not sequence of characters representing some integer value in decimal. So if the next input character is '0', the value read will be the character value '0', which is probably 48.

Since a typedef creates an alias for an existing type, not a new distinct type, there's no way for the >> operator to know that you want to treat int8_t as an integer type rather than as a character type.

The problem is that in most implementations there is no 8-bit integer type that's not a character type.

The only workaround is to read into an int variable and then convert to int8_t (with range checks if you need them).

Incidentally, int8_t is a signed type; the corresponding unsigned type is uint8_t, which has a range of 0..255.

(One more consideration: if CHAR_BIT > 8, which is permitted by the standard, then neither int8_t nor uint8_t will be defined at all.)

Armure answered 7/7, 2014 at 19:4 Comment(7)
It would be nicer, I think, if the _t types weren't allowed to typedef to char (but unsigned and signed char were fine). It would stop this from being implementation-defined.Corinacorine
@Keith: Thank you for your answer. I will read an int k and cast it: j=(int8_t)k.Tundra
@JosephMansfield: That wouldn't help; cin >> ... treats unsigned char and signed char as character types.Armure
@Excalibur: There's no need for a cast. Just assign it; the conversion is done implicitly.Armure
@KeithThompson Oh. Well I think that sucks too. char is a distinct type entirely because it is supposed to be treated as a special character type.Corinacorine
@Excalibur: Think about what you want to do if the input is, say, 1000, which doesn't fit in an int8_t or uint8_t.Armure
@JosephMansfield: A substantial body of code needs an unsigned type that is guaranteed to be exactly eight bits with no padding and has the same exemption from aliasing rules as unsigned char. Since uint8_t is the only standard-defined type which, if it exists, is guaranteed to be an 8-bit unsigned value with no padding, and since any implementation which would be allowed to define that type would be able to make it use the same aliasing rules as unsigned char, a lot of code uses uint8_t when it needs a type having both characteristics.Bucephalus
B
4

int8_t and uint8_t are almost certainly character types (Are int8_t and uint8_t intended to behave like a character?) so std::cin >> j will read a single character from stdin and interpret it as a character, not as a number.

Briny answered 7/7, 2014 at 19:3 Comment(0)
E
0

int8_t is likely the same as char, which means cin >> j will simply read a single character ('6') from input and store it in j.

Endlong answered 7/7, 2014 at 19:4 Comment(1)
int8_t cannot be the same type as char. It's most likely the same type as signed char.Armure
Y
0

int8_t is defined as a typedef name for signed char. So operator >> used with an object of type int8_t behaves the same way as it would be used for an object of type signed char

Yokefellow answered 7/7, 2014 at 19:6 Comment(5)
It could be a typedef for either signed char or plain char.Armure
@Keith Thompson No it may not because how type char will behave depends on compiler options. Some compilers by default implement type char as unsigned char.Yokefellow
Defining int8_t as plain char is perfectly legal if plain char is signed. If the signedness of plain char depends on a compiler option, the definition of int8_t could be controlled by an #if test on, for example, CHAR_MAX. It's certainly easier to define it as signed char, but implementations aren't required to be sensible.Armure
@Keith Thompson It is a wrong statement. char is not an unsigned integer type while uint8_t shall be defined as unsigned integer type.Yokefellow
You're right. char is an integer type, and it's either signed or unsigned, but it's neither a signed integer type nor an unsigned integer type (a quirk of the way those terms are defined in the C and C++ standards). Conceivably int8_t could be a typedef for an extended signed integer type (for example, an implementation could define an 8-bit non-character type for just the problem raised in this question). And if CHAR_BIT > 8, then int8_t will not be defined at all.Armure
W
0

The _t types aren't first class types, they are typedef aliases the observe certain constraints, e.g. int8_t is a type that can store a signed, 8-bit value.

On most systems, this will mean they are typedefd to char. And because they are a typedef and not a first-class type, you are invoking cin.operator<<(char) and cin.operator>>(char).

When you input "65", cin.operator>>(char) consumes the '6' and places it's ascii value, 54, into variable j.

To work around this you'll need to use a different type, possibly the easiest method being just to use a larger integer type and apply constraints and then cast down:

int8_t fetchInt8(const char* prompt) {
    int in = 0;
    for ( ; ; ) { // endless loop.
        std::cout << prompt << ": ";
        std::cin >> in;
        if (in >= std::numeric_limits<int8_t>::min()
              && in <= std::numeric_limits<int8_t>::max()) {
            std::cout << "You entered: " << in << '\n';
            // exit the loop
            break;
        }
        std::cerr << "Error: Invalid number for an int8\n";
    }

    return static_cast<int8_t>(in);
}

Note that int8_t is signed, which means it stores -128 thru +127. If you want only positive values, use the uint8_t type.

Whichsoever answered 7/7, 2014 at 19:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.