Why on Linux is uint64_t defined as unsigned long int, while on Windows it is unsigned long long int?

153 Views Asked by At

I need to use uint64_t as unsigned long long int due to the fact that I need to fit in numbers larger than unsigned long in it? (both OSes are 64-bit), how can I fix this? Here is the code which impleements binary division: Here is the function where I tried to print to see the result of the final function:

void binary_div(s21_big_decimal *value_1, s21_big_decimal value_2, s21_big_decimal *result) {
  init_s21_decimal(result);
  s21_big_decimal Q;
  s21_big_decimal R;
  int scaleDiv = -1;
  do {
    init_s21_decimal(&R);
    init_s21_decimal(&Q);
    scaleDiv++;
    int lastBit = (SIZE_BIG_DECIMAL - 1) * 32 - 1;
    int currentBit = get_bit_big(*value_1, lastBit);
    while (currentBit == 0 && lastBit >= 0) {
      currentBit = get_bit_big(*value_1, lastBit);
      if (currentBit == 0) lastBit--;
    }
    for (int i = lastBit; i >= 0; i--) {
      change_left_big(&R, 1);
      set_bit_big(&R, 0, get_bit_big(*value_1, i));
      if (is_less_or_equal_big(value_2, R)) {
        s21_big_decimal res = {0};
        init_s21_decimal(&res);
        s21_sub_big(R, value_2, &res);
        init_s21_decimal(&R);
        copy_bits(res, &R);
        set_bit_big(&Q, i, 1);
      }
    }
    if (scaleDiv > 0) {
      mul_10_big(result);
    }
    s21_big_decimal res;
    init_s21_decimal(&res);
    add_big(*result, Q, &res);
    copy_bits(res, result);
    if (!is_zero_big(R)) {
      init_s21_decimal(value_1);
      mul_10_big(&R);
      copy_bits(R, value_1);
    }
  } while (!is_zero_big(R) && scaleDiv < 28);
  set_scale_big(result, scaleDiv);
  for (int i = 0; i < 7; i++) {
    printf("%lX ", result->bits[i]);
  }
  printf("\n");
}

I work with bits here is example of the function mul_10_big:

int mul_10_big(s21_big_decimal *dec)
{
  s21_big_decimal temp = *dec;
  for (int i = 0; i < SIZE_BIG_DECIMAL - 1; i++)
  {
    temp.bits[i] *= 10;
  }
  temp.scale++;
  int overflowed = 0;
  if (getoverflow(&temp))
  {
    overflowed = 1;
  }
  else
  {
    *dec = temp;
  }
  return overflowed;
}
int div_10_big(s21_big_decimal *dec)
{
  uint64_t remained = 0;
  for (int i = SIZE_BIG_DECIMAL - 2; i >= 0; i--)
  {
    dec->bits[i] += remained << 32;
    remained = dec->bits[i] % 10;
    dec->bits[i] /= 10;
  }
  dec->scale--;
  return remained;
}
int add_big(s21_big_decimal big_dec1, s21_big_decimal big_dec2,
            s21_big_decimal *result)
{
  int res = 0;
  normalization_big(&big_dec1, &big_dec2);
  for (int i = 0; i < SIZE_BIG_DECIMAL - 1; i++)
  {
    result->bits[i] += big_dec1.bits[i] + big_dec2.bits[i];
    getoverflow(result);
  }
  return res;
}
1

There are 1 best solutions below

2
chqrlie On

uint64_t is unsigned long on linux and macOS because the type unsigned long has 64 bits so there is no need to use a type with a larger rank such as unsigned long long eventhough this later type also has 64 bits.

On Windows, uint64_t cannot be typedefed to unsigned long because for legacy reasons, Microsoft chose to keep unsigned long as a 32-bit type even on its 64-bit ABIs.

If you need to handle numbers larger than 4294967295, you should indeed not use unsigned long that may be limited to 32 bits. Using uint64_t that has exactly 64 bits or unsigned long long that has at least 64 bits are the correct choices. uint64_t might not be available on all platforms, but is on your targets and you are unlikely to ever encounter one that does not.

Whether uint64_t is typedefed to unsigned long or unsigned long long does not matter for your purpose as it always has 64 bits.

Changing the type uint64_t is not an option as you will get potential undefined behavior using different definitions in different modules.

If you use integer types consistently in your application, using uint64_t everywhere you need a 64-bit unsigned type should not pose a problem.

For reading and printing, you can either use the standard macros defined in <inttypes.h>:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(int argc, char *argv[]) {
    uint64_t x;
    if (argc > 1 && sscanf(argv[1], "%"SCNu64"", &x) == 1) {
        printf("%16"PRIx64"\n", x);
    }
    return 0;
}

If you prefer, you can use strtoull and printf this way:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(int argc, char *argv[]) {
    char buf[100];
    uint64_t x;
    if (argc > 1) {
        x = strtoul(argv[1], NULL, 10);
        printf("%16llx"\n", (unsigned long long)x);
    }
    return 0;
}

You could also define your own type u64_t as a typedef for unsigned long long and use %llu or %llx in scanf and printf like functions without extra casts.

#include <stdio.h>
#include <stdlib.h>

typedef unsigned long long u64_t;  // assuming 64-bit

int main(int argc, char *argv[]) {
    u64_t x;
    if (argc > 1 && sscanf(argv[1], "%llu", &x) == 1) {
        printf("%16llx\n", x);
    }
    return 0;
}

Or you can simply use unsigned long long everywhere.