Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any non-readability related reasons to NOT specifically use fixed width integers every single time?

Tags:

c++

c

Let's say that we have uint_least8_t var, where, hypothetically speaking, var won't possibly ever exceed the value 255. I know that's not how programming works and "possibly" and "ever" are a blasphemy, but, aside from complicating the code and making it less readable, what makes always using fixed width integers a bad idea?

like image 706
Galan Lucian Avatar asked Dec 05 '18 17:12

Galan Lucian


2 Answers

Compatibility with other code, and not imposing arbitrary restrictions.

A large portion of the integer variables you use in a typical C program are array indices, object counts, etc. Such values are inherently not representable as fixed-width integer types whose width is known at the time of writing the program; the possible range varies by implementation. (This is related to the fact that a given C implementation is not Turing-equivalent; only an infinite family of implementations, via the Transdichotomous model is). Cetainly you could decide that you only want to support up to 2^32 (for example) of a given thing, and thereby get by with using a fixed-width type, but it's anti-idiomatic, and such code won't be compatible with typical (including std lib) interfaces that use size_t and might be passing or accepting larger objects.

like image 39
R.. GitHub STOP HELPING ICE Avatar answered Dec 04 '22 07:12

R.. GitHub STOP HELPING ICE


Performance is another reason.

Narrow operands require additional narrowing/widening instructions. This can't always be optimized away without side effects. And sometimes the optimizer just isn't smart enough and plays it safe.

Take the following contrived example.

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono_literals;

int main()
{
    auto tm1 = chrono::high_resolution_clock::now();
    unsigned int n = 0;
    unsigned int x = 0;  // though, uint8_t would have been enough!
    for (unsigned int i = 0; i < 1000000000; i++) {
        n += (x * i);
        x = (n + 1) & 0x7F;
    }
    auto tm2 = chrono::high_resolution_clock::now();
    cout << n << ", " << (tm2 - tm1) / 1.0s << " s" << endl;
}

If we change the type of x from unsigned int to uint8_t, the application becomes 15% slower (2s instead of 1.7s run time on x86-64 when compiled with GCC 7.2 -O3 full optimization on).

Assembly with a 32-bit x:

.L2:
  imul eax, edx
  inc edx
  add ebx, eax
  lea eax, [rbx+1]
  and eax, 127
  cmp edx, 1000000000
  jne .L2

Assembly with an 8-bit x:

.L2:
  movzx eax, al    ; owww!
  imul eax, edx
  inc edx
  add ebp, eax
  lea eax, [rbp+1]
  and eax, 127
  cmp edx, 1000000000
  jne .L2
like image 178
rustyx Avatar answered Dec 04 '22 08:12

rustyx