The difference between uint8_t, uint_fast8_t and uint_least8_t

The C99 standard provides the following data types. The documentation can be found here for the stdint AVR library.

  • uint8_t means an 8-bit unsigned type.
  • uint_fast8_t means that it is the fastest unsigned int with at least 8 bits.
  • uint_least8_t means it has an unsigned int with at least 8 bits.

I understand uint8_t and what is uint_fast8_t (I don’t know how it is implemented at the register level).

1. Can you explain what “it is a unsigned int with at least 8 bits” means?

2. How do uint_fast8_t and uint_least8_t help improve performance / code space compared to uint8_t ?

+56
c ++ c c99 avr integer
Jan 28 '16 at 7:15
source share
6 answers

uint_least8_t is the smallest type that has at least 8 bits. uint_fast8_t is the fastest type that has at least 8 bits.

You can see the differences by introducing exotic architectures. Imagine a 20-bit architecture. Its unsigned int has 20 bits (one register), and its unsigned char has 10 bits. Therefore, sizeof(int) == 2 , but using char types, additional instructions are needed to reduce the registers in half. Then:

  • uint8_t : undefined (without 8-bit type).
  • uint_least8_t : unsigned char , the smallest type that contains at least 8 bits.
  • uint_fast8_t : unsigned int , because in my imaginary architecture, a half-register variable is slower than a full record.
+65
Jan 28 '16 at 7:24
source share

The theory looks something like this:

uint8_t requires exactly 8 bits, but it should not exist. Therefore, you should use it where you rely on arithmetic behavior modulo 256 for an 8-bit integer and where you would rather compile the rejection of incorrect behavior on obscure architectures.

uint_least8_t should be the smallest available unsigned integer type that can hold at least 8 bits. You would use it when you want to minimize memory usage by such things as large arrays.

uint_fast8_t must be the "fastest" unsigned type, which can store at least 8 bits; however, in reality it is not guaranteed to be the fastest for any given operation on any given processor. You would use it in processing code that performs many operations on a value.

The practice is that the “fast” and “least” types are not used much.

The "least" types are really useful if you care about portability in order to mask architectures with CHAR_BIT! = 8, which most people do not.

The problem with the "fast" types is that the "fastest" is hard to copy. A smaller type may mean less load on the memory / cache system, but using a type that is smaller than native may require additional instructions. Also, what can best change between versions of the architecture, but developers often want to avoid breaking ABI in such cases.

From a glance at some popular implementations, it seems that the definitions of uint_fastn_t are pretty arbitrary. glibc, apparently, defines them as at least the "native word size" of the system in question, without taking into account the fact that many modern processors (especially 64-bit) have some support for fast operations on elements smaller than their native word size. IOS seems to define them as equivalent to fixed-size types. Other platforms may vary.

In general, if your performance of hard code with small integers is your goal, you should be in place on the platforms you need, with different types, to see what works best.

+20
Jan 28 '16 at 12:03
source share

uint8_t means: give me unsigned int exactly 8 bits.

uint_least8_t means: give me the smallest type of unsigned int, which has at least 8 bits. Optimize memory consumption.

uint_fast8_t means: give me unsigned int at least 8 bits. Choose a larger type if it speeds up my program due to alignment considerations. Optimize your speed.

In addition, unlike simple int types, the signed version of the stdint.h types mentioned above is guaranteed to be 2 additional formats.

+18
Jan 28 '16 at 7:26
source share

1. Can you explain what “it is an unsigned int with at least 8 bits” means?

This should be obvious. This means that it is an unsigned integer type and its width is at least 8 bits. In fact, this means that it can at least hold numbers from 0 to 255, and it can definitely not contain negative numbers, but it can hold numbers above 255.

Obviously, you should not use any of these types if you plan to store any number outside the range from 0 to 255 (and you want it to be portable).

2.How do uint_fast8_t and uint_least8_t help improve performance / code space compared to uint8_t?

uint_fast8_t is required faster, so you should use this if you want the code to be fast. uint_least8_t , on the other hand, requires that there is no smaller candidate, so you should use this if size is a problem.




And of course, you only use uint8_t when you absolutely require that it be exactly 8 bits. Using uint8_t may make the code not portable, since uint8_t not required to exist (because such a small integer type does not exist on some platforms).

+4
Jan 28 '16 at 7:48
source share

Fast integer types are defined as the fastest integer available with at least the number of bits (in your case 8).

The platform can define uint_fast8_t as uint8_t , then there will be no difference in speed.

The reason is that there are platforms that are slower if they do not use their native word length.

+3
Jan 28 '16 at 7:26
source share

Some processors may not work as efficiently on small data types as on large ones. For example, given:

 uint32_t foo(uint32_t x, uint8_t y) { x+=y; y+=2; x+=y; y+=4; x+=y; y+=6; x+=y; return x; } 

if y were uint32_t , the compiler for ARM Cortex-M3 could just generate

 add r0,r0,r1,asl #2 ; x+=(y<<2) add r0,r0,#12 ; x+=12 bx lr ; return x 

but since y is uint8_t , the compiler will have to generate:

 add r0,r0,r1 ; x+=y add r1,r1,#2 ; Compute y+2 and r1,r1,#255 ; y=(y+2) & 255 add r0,r0,r1 ; x+=y add r1,r1,#4 ; Compute y+4 and r1,r1,#255 ; y=(y+4) & 255 add r0,r0,r1 ; x+=y add r1,r1,#6 ; Compute y+6 and r1,r1,#255 ; y=(y+6) & 255 add r0,r0,r1 ; x+=y bx lr ; return x 

The alleged purpose of the “fast” types was to allow compilers to replace smaller types that could not be efficiently handled with faster ones. Unfortunately, the semantics of the “fast” types are rather poorly specified, which in turn leaves gloomy questions about whether expressions will be evaluated using signed or unsigned mathematics.

+2
Jan 28 '16 at 23:59
source share



All Articles