Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

uint64_t or int64_t for bitwise operations?

Tags:

c

I'm writing some code which uses an array initialized with bit shifts by the compiler. I want the compiler to do the work instead of initializing the array with a for loop. I am also using the LL suffix here so that it will be 64 bit cross platform.

Is there any problem with using int64_t instead of uint64_t here?

Is there a more idiomatic or correct way to write code like this? edit: here is the entire program since you asked about use case. I don't include error checking in my code since it reads from a fixed data set.

The full problem description is here https://adventofcode.com/2022/day/3

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdint.h>

const int64_t map[] = {
    ['a'] = 1LL<<1,  ['b'] = 1LL<<2,
    ['c'] = 1LL<<3,  ['d'] = 1LL<<4,
    ['e'] = 1LL<<5,  ['f'] = 1LL<<6,
    ['g'] = 1LL<<7,  ['h'] = 1LL<<8,
    ['i'] = 1LL<<9,  ['j'] = 1LL<<10,
    ['k'] = 1LL<<11, ['l'] = 1LL<<12,
    ['m'] = 1LL<<13, ['n'] = 1LL<<14,
    ['o'] = 1LL<<15, ['p'] = 1LL<<16,
    ['q'] = 1LL<<17, ['r'] = 1LL<<18,
    ['s'] = 1LL<<19, ['t'] = 1LL<<20,
    ['u'] = 1LL<<21, ['v'] = 1LL<<22,
    ['w'] = 1LL<<23, ['x'] = 1LL<<24,
    ['y'] = 1LL<<25, ['z'] = 1LL<<26,
    ['A'] = 1LL<<27, ['B'] = 1LL<<28,
    ['C'] = 1LL<<29, ['D'] = 1LL<<30,
    ['E'] = 1LL<<31, ['F'] = 1LL<<32,
    ['G'] = 1LL<<33, ['H'] = 1LL<<34,
    ['I'] = 1LL<<35, ['J'] = 1LL<<36,
    ['K'] = 1LL<<37, ['L'] = 1LL<<38,
    ['M'] = 1LL<<39, ['N'] = 1LL<<40,
    ['O'] = 1LL<<41, ['P'] = 1LL<<42,
    ['Q'] = 1LL<<43, ['R'] = 1LL<<44,
    ['S'] = 1LL<<45, ['T'] = 1LL<<46,
    ['U'] = 1LL<<47, ['V'] = 1LL<<48,
    ['W'] = 1LL<<49, ['X'] = 1LL<<50,
    ['Y'] = 1LL<<51, ['Z'] = 1LL<<52,
};

int priority(char *s)
{
    char    *end = s + strlen(s) - 1;
    char    *mid = s + (end - s) / 2;
    int64_t x, y;

    for (x = 0; s < mid; s++)
        x |= map[*s];
    for (y = 0; s < end; s++)
        y |= map[*s];
    return log2(x & y);
}

int main()
{
    FILE    *fp;
    char    buf[BUFSIZ];
    int     sum = 0;

    fp = fopen("data", "r");
    while (fgets(buf, sizeof buf, fp))
        sum += priority(buf);
    printf("%d\n", sum);
}
like image 695
bjarne Avatar asked Sep 01 '25 03:09

bjarne


1 Answers

Is there any problem with using int64_t instead of uint64_t here?

No.
All elements of the array are zero or more and <= INT64_MAX and so fit in both int64_t and uint64_t.

Should code have a shift of 63, use array element type uint64_t and 1ULL<<63.


Is there a more idiomatic or correct way to write code like this?

Looks OK, even though it appears space inefficient.

In general, better to use unsigned types and constants for masks.

Note that the array has 123 elements (assuming ASCII and 'z' is 122).

OP posted a sample use case.
Alternative to consider (ASCII dependent):

// int64_t x, y;
//   x |= map[*s];
// return log2(x & y);

// Something like:
uint64_t x, y;        //   v------------v swap UC and LC letters 
  x |= UINT64_C(1) << ((*s ^ ('A' ^ 'a')) & 63);
unsigned p = stdc_first_leading_one_ull(x & y);  // Avoid FP math for an integer task.
if (p >= 32) p -= 6; // Adjust priority for the gap between UC and LC letters. 
return p;
// Could even remove the `^` in the loop with a little post-loop code.

Pedantic: "using the LL suffix here so that it will be 64 bit cross platform" --> LL will use at least 64-bits. Perhaps a future system will use 128-bit for long long.
I'd use unsigned types and constants and even consider UINT64_C(1)<< xx - which is still at least 64-bits, but doubtful to ever be 128-bit, even if long long is.
Either way, as long as the value fits in the target array element type, I would not expect a warning.

like image 145
chux - Reinstate Monica Avatar answered Sep 02 '25 15:09

chux - Reinstate Monica