0

I've been working with C++ and have been utilizing the UBA sanitizer to convert a double to an unsigned long long. However, I've been encountering an issue when the value is negative, which results in the error message: "runtime error: value -2 is outside the range of representable values of type 'long long unsigned int'"

The code is something like this:

unsigned long long valueULL = 0;
double value = -2;
valueULL = (unsigned long long)value;

I tried to use this cast instead and it doesn't help:

valueULL = std::make_unsigned_t<unsigned long long>(value);

Is there a way to cast it without encountering this error?

Raz Cohen
  • 79
  • 1
  • 7
  • What is UBA sanitizer? Could you use a usual sanitizer name? – 273K Jun 22 '23 at 04:58
  • The sanitizer is The Undefined Behavior Sanitizer - UBSAN. – Raz Cohen Jun 22 '23 at 05:29
  • Your description is not particularly clear, but I assume `value` has type `double` (and value `2.0`). Converting a `double` to an integral type has two steps - first, discard any fractional part (truncate toward zero). From there, if the truncated value cannot be represented using the integral type, behaviour is undefined. That's what your sanitiser is detecting (since `-2.0` is outside the range any `unsigned` type can represent). As to what you need to do - that depends on what you expect a conversion of `-2.0` to an unsigned type to produce, and you haven't described that at all. – Peter Jun 22 '23 at 06:10
  • Have you tried -2U? – 273K Jun 22 '23 at 06:10
  • I edited the code in the question. – Raz Cohen Jun 22 '23 at 06:41
  • What do you want the result to be? – n. m. could be an AI Jun 22 '23 at 06:48
  • 1
    There is probably only one reasonable result and "two-step cast" that has been suggested namely `(unsigned long long)(long long)value` will give you that – harold Jun 22 '23 at 06:54

1 Answers1

1

It seems that you're referring to clang's -fsanitize=undefined UBSanitizer. The sanitizer is correct in this case, and your code really does contain undefined behavior:

A prvalue of a floating-point type can be converted to a prvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.

- [conv.fpint] §1

You can fix your undefined behavior with a two-step cast:

double value = -2;
auto valueULL = static_cast<unsigned long long>(static_cast<long long>(value));

Or alternatively, you could call std::llround:

auto valueULL = static_cast<unsigned long long>(std::llround(value));

Note: this will produce a very great value, namely ULLONG_MAX - 2. If this is what you actually want, then these solutions make sense. Otherwise, you should listen to the sanitizer and make sure that your double doesn't turn negative before converting to unsigned types.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96