1

If I understand well, int_fast_n_t types are guaranteed to be at least n bits long. Depending on the compiler and the architecture of the computer these types can also be defined on more than n bits. For instance, a int_fast_8_t could be interpreted as a 32 bits int.

Is there some kind of mechanism which enforces that the value of an int_fast_n_t never overflow even if the true type is defined on more than n bits?

Consider the following code for example:

int main(){
    int_fast8_t a = 64;
    a *= 2; // -128
    return 0;
}

I do not want a to be greater than 127. If a is interpreted as a "regular" int (32 bits), is it possible that a exceed 127 and be not equal to -128?

Thanks for your answers.

Papipone
  • 1,083
  • 3
  • 20
  • 39
  • 1
    No. You could cast the result to something which really is 8 bits, though. – nemequ Feb 20 '18 at 22:35
  • Is `int_fast8_t` really 32 bits? The name suggest 8 bit so it can only hold 127 as positive number. Casting to `(char)` should do the trick! Typically `char` is only 8 bits. – sg7 Feb 20 '18 at 22:35
  • @sg7: Depends on the platform. On my machine uint_fast16/32_t are 64 bits, though uint_fast8_t is 8. – nemequ Feb 20 '18 at 22:38
  • 1
    @sg7 it means "A type which is at least 8 bits and is the fastest for holding 8-bit data". It could be any size 8 or greater, and it has the properties of its actual size (not the properties of an 8-bit integer) – M.M Feb 20 '18 at 22:46

5 Answers5

2

It is absolutely possible for the result to exceed 127. int_fast8_t (and uint_fast8_t and all the rest) set an explicit minimum size for the value, but it could be larger, and the compiler will not prevent it from exceeding the stated 8 bit bounds (it behaves exactly like the larger type it represents, the "8ness" of it isn't relevant at runtime), only guarantee it can definitely represent all values in said 8 bit range.

If you need it to explicitly truncate/wrap to 8 bit values, either use (or cast to) int8_t to restrict the representable range (though overflow wouldn't be defined), or explicitly use masks to perform the same work yourself when needed.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
2
int_fast8_t a = 64;
a *= 2;

If a is interpreted as a "regular" int (32 bits), is it possible that a exceed 127 and be not equal to -128?

Yes. It very likely a * 2 will save in a as 128. I would expect this on all processors unless the processor was an 8-bit one.

Is there some kind of mechanism which enforces that the value of an int_fast_n_t never overflow ?

No. Signed integer overflow is still possible as well as values outside the [-128...127] range.

I do not want a to be greater than 127

Use int8_t. The value save will never exceed 127, yet code still has implementation defined behavior in setting a 128 to an int8_t. This often results in -128 (values wrap mod 256), yet other values are possible (this is uncommon).

int8_t a = 64;
a *= 2;

If assignment to int8_t is not available or has unexpected implementation defined behavior, code could force the wrapping itself:

int_fast8_t a = foo();  // a takes on some value
a %= 256; 
if (a < -128) a += 256;
else if (a > 127) a -= 256;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • int8_t, int16_t, int32_t, int64_t are not guaranteed to be defined on every platform? – Papipone Feb 20 '18 at 22:54
  • 2
    @Papipone That is true, yet OP does not appear to be on such an esoteric machine. If we venture to non-8-bit `char`, we might as well consider _sign magnitude_ and _one's complement_, and traps. Likely beyond OP''s need. – chux - Reinstate Monica Feb 20 '18 at 23:04
1

Nope. All the fast types really are are typedefs. For example, stdint.h on my machine includes

/* Fast types.  */

/* Signed.  */
typedef signed char     int_fast8_t;
#if __WORDSIZE == 64
typedef long int        int_fast16_t;
typedef long int        int_fast32_t;
typedef long int        int_fast64_t;
#else
typedef int         int_fast16_t;
typedef int         int_fast32_t;
__extension__
typedef long long int       int_fast64_t;
#endif

/* Unsigned.  */
typedef unsigned char       uint_fast8_t;
#if __WORDSIZE == 64
typedef unsigned long int   uint_fast16_t;
typedef unsigned long int   uint_fast32_t;
typedef unsigned long int   uint_fast64_t;
#else
typedef unsigned int        uint_fast16_t;
typedef unsigned int        uint_fast32_t;
__extension__
typedef unsigned long long int  uint_fast64_t;
#endif

The closest you can come without a significant performance penalty is probably casting the result to an 8-bit type.

nemequ
  • 16,623
  • 1
  • 43
  • 62
1

Just use unsigned char if you want to manipulate 8 bits (unsigned char is one byte long) you will work on 0 to 0xFF (255) unsigned range

Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
  • unsigned char (or char for that matter) is only 8 bits if CHAR_BIT == 8. There is no type in C which is guaranteed to exist and be 8-bits. – nemequ Feb 20 '18 at 23:40
  • Hi @nemequ, are you sure? I have never heard of any situation where char size differs from 8 bits. – Antonin GAVREL Feb 21 '18 at 15:52
  • 1
    Yes. They're very rare in anything even remotely modern (4 and 6 bits was common up to ~ 1970), but AFAIK some specialized processors do still use something other than 8 bits. More importantly, the C standard does not require 8 bits, otherwise why include a CHAR_BIT macro? – nemequ Feb 21 '18 at 17:24
1

From the C(99) standard:

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.

So use int8_t to guarantee 8 bit int.

A compliant C99/C11 compiler on a POSIX platform must have int8_t.

sg7
  • 6,108
  • 2
  • 32
  • 40
  • 1
    "`int8_t` to guarantee 8 bit int" is true yet `int8_t b = 128;` is not specified to store -128. Although that is very common. – chux - Reinstate Monica Feb 20 '18 at 23:05
  • @chux Thank you for the comment. My understanding is that `int8_t` has to have two’s complement representation as per standard. And the following source: `https://en.wikibooks.org/wiki/C_Programming/stdint.h` specifies the range as `<-128,127>` – sg7 Feb 20 '18 at 23:16
  • The issue is not about `int8_t`. The concern is _conversion_ of a value, like 128, that is outside the target range [-128...127] - which is _implementation defined behavior_. Even with `int8_t` specified as you say, a complier _could_ assign 127 (or other values) to `int8_t c`. I doubt such compilers practically exist except for hostile ones, that are designed to stress test code. – chux - Reinstate Monica Feb 20 '18 at 23:26
  • `int8_t` is optional in C99/C11; does POSIX specify that it must exist? Also, the representation of signed integers is not defined; two's complement is pretty universal on modern CPUs, but not technically required. – nemequ Feb 20 '18 at 23:35
  • 2
    @nemequ `int8_t is optional in C99/C11;` TRUE. However, POSIX requires `int8_t` to be defined, source: https://www.unix.com/man-page/posix/7posix/stdint.h/ – sg7 Feb 20 '18 at 23:44
  • Hmmm, strange POSIX makes `(u)int64_t` optional, yet `(u)int8,16,32_t`, `uint_least8,16,32,64_t`, `uint_least8,16,32,64_t` required. – chux - Reinstate Monica Feb 21 '18 at 02:34