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);
}
Is there any problem with using
int64_t
instead ofuint64_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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With