-2

I'm trying to find the smallest int type in C++. I read that there are no types which use less than a byte, because is the minimum addressable size. The smallest one I found is int8_t, or _int8 visual studio type. I tried to use both, but the program stores the values as chars. There are a way to use this types as integers? Or even, if there are a way to use smaller types (2 bits (signed) would be perfect XD, I need to store only -1, 0 and 1), or any other numeric byte type.

Thanks in advance

Community
  • 1
  • 1
selmo12f
  • 3
  • 4
  • What you are asking is: *is there a way to store a char so that it is not a char?* – Mark Jansen Aug 25 '15 at 11:51
  • What does "stores the values as chars" mean? It's not clear what your objection to using `int8_t` is. – David Schwartz Aug 25 '15 at 11:53
  • I'm not much a pro on VS, but your _int8 is simply a char with a different name to improve code readability. For smaller type, use std::bitset maybe, depending on your needs – MokaT Aug 25 '15 at 11:53
  • If you only want to store 0 and 1, you can use an implementation similar to `std::vector`, where 8 bit values can be stored in one byte. Otherwise, you would have to come up with a mini hack to make it work for more values. – Joe Aug 25 '15 at 11:54
  • Or to put it the other way, a char is simply a signed [usually] 8-bit integer. You _can_ store 2-bit entities, but only within a larger whole (search _struct_ and _bitfield_ -- for example [this wiki page](https://en.wikipedia.org/wiki/Bit_field)). – TripeHound Aug 25 '15 at 11:55

4 Answers4

4

The standard gives you the following types:

  • signed char
  • unsigned char

(with char being equivalent to one of these)

And your fixed-width int8_t, uint8_t on an 8-bit system are simply aliases for these.

They are all integers already.

Your statement "but the program stores the values as chars" suggests a misunderstanding of what values with these types fundamentally are. This may be due to the following.

The only "problem" you may encounter is the special-case formatting for these types in the IOStreams sublibrary:

const char c = 40;
std::cout << c << '\n';
// Output: "("

Since int8_t is/may be signed char, this is literally the same:

const int8_t c = 40;
std::cout << c << '\n';
// Output: "("

But this is easily "fixable" with a little integral promotion:

const char c = 40;
std::cout << +c << '\n';
// Output: 40

Now the stream gets an int, and lexically-casts it as part of its formatting routine.

This has to do with the type system and the function overloads provided by the standard library; it has nothing to do with "how the values are stored". They are all integers.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    As you told the problem was the IOStreams representation. I saw the output, and that the processor set 'char' as value type, and didn't know that for all the other operations the chars are intergers. Thanks a lot ;-) – selmo12f Sep 11 '15 at 10:35
  • Minor side note: while `char` is equivalent to either `signed char` or `unsigned char` in most aspects, it is not an alias (e.g. not typedef) but a distinct type. E.g. you can have 3 overloads: `f(char)`, `f(signed char)`, `f(unsigned char)`. – Pablo H Sep 28 '18 at 14:26
  • @PabloH That's correct. "Is equivalent to", not "is". – Lightness Races in Orbit Oct 01 '18 at 12:48
2

I tried to use both, but the program stores the values as chars.

It doesn't (store the values as chars). In C++, a char value is an integer value. The difference to other integer types, is in two places:

  • the compiler converts char literals to integers transparently

  • the standard library treats char type separately from other integer types (that is, it considers char to represent letters text characters, not numbers).

As far as the compiler is concerned, the code:

char x = 'A';

is equivalent to:

char x = 65; // set value to 65 (ASCII code for letter "A")

If you look in the debugger at the second definition of x, is probable the debugger/IDE will tell you x is 'A'.

Or even, if there are a way to use smaller types (2 bits (signed) would be perfect XD, I need to store only -1, 0 and 1), or any other numeric byte type.

There is no integer type with a smaller than 8 bits representation.

That said, you can (and probably should) create a custom type for it:

enum class your_type: std::uint8_t
{
    negative = -1,
    neutral = 0,
    positive = 1
};

Choose values that make sense in the context of your program (i.e. not "negative, neutral and positive").

utnapistim
  • 26,809
  • 3
  • 46
  • 82
1

You can use a bit field with two bits.

sweerpotato
  • 470
  • 2
  • 11
  • 22
1

With MSVC you could use #pragma pack(1) and structs to obtain this.

#pragma pack(1)
struct int2 {
    int value:2;
};
#pragma pack()

int main()
{
    int2 a;
    for (int i = 0; i < 10; i++)
    {
        a.value = i;
        std::cout << a.value << std::endl;
    }

    return 0;
}

Output:

0
1
-2
-1
0
1
-2
-1
0
1

I guess building constructors and operator functions to access the value indirectly shouldn't be a problem.

EDIT: Improved version with operators:

#pragma pack(1)
template <typename INT_TYPE, int BYTE_SIZE>
class int_x {
    INT_TYPE value: BYTE_SIZE;
public:
    int_x() :value(0) {}
    int_x(INT_TYPE v) :value(v) {}
    operator INT_TYPE() const {
        return value;
    }
    int_x& operator=(INT_TYPE v) {
        value = v; return *this;
    }
    int_x& operator+=(INT_TYPE v) {
        value += v; return *this;
    }
    int_x& operator-=(INT_TYPE v) {
        value -= v; return *this;
    }
    int_x& operator/=(INT_TYPE v) {
        value /= v; return *this;
    }
    int_x& operator*=(INT_TYPE v) {
        value *= v; return *this;
    }
};
typedef int_x<int, 1> int1;             //Range [-1;0]
typedef int_x<unsigned int, 1> uint1;   //Range [0;1]

typedef int_x<int, 2> int2;             //Range [-2;1]
typedef int_x<unsigned int, 2> uint2;   //Range [0;3]

typedef int_x<int, 3> int3;             //Range [-4;3]
typedef int_x<unsigned int, 3> uint3;   //Range [0;8]
#pragma pack()
Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
  • gcc supports this as well: https://gcc.gnu.org/onlinedocs/gcc/Structure-Packing-Pragmas.html – Simon Kraemer Aug 25 '15 at 13:09
  • Isn't your approach undefined? As the value is not undefined and you're relying on wrap-around semantics for the demonstration. – Lightness Races in Orbit Sep 11 '15 at 14:45
  • What exactly do you mean? My "demonstration" is only to show the overflow. What do you think is undefined? – Simon Kraemer Sep 11 '15 at 15:50
  • Signed integer overflow. – Lightness Races in Orbit Sep 11 '15 at 17:13
  • `[C++11: 4.7/3]` renders the conversion back to `int2::value` (during your assignment operation) as undefined. Only _unsigned_ integer "overflow" is well-defined (and, as such, is not an overflow at all). Therefore, your program has UB and _strictly speaking_ doesn't demonstrate anything reliably ;) – Lightness Races in Orbit Sep 11 '15 at 17:15
  • Ah. Yes, the overflow itself is UB if the base type is signed. But the overflow in this case was only used to show the range of the int_x specialisation. So the approach itself is defined behaviour but my method to show the behaviour is based in UB. – Simon Kraemer Sep 11 '15 at 21:30
  • Additionally I stated that the (original) approach is based MSVC. Even if behaviour is undefined in the standard it can still be defined or at least work in a comprehensible way for a specific implementation of the standard. – Simon Kraemer Sep 11 '15 at 21:34
  • Yes that's what I said - the demo invokes UB so the whole thing is suspect. _Technically_. :) – Lightness Races in Orbit Sep 11 '15 at 23:58