3

So this is probably a really simple question and if it was not about C++ I would just go ahead and check if it works on my computer or not, but unfortunately in C++ things usually tend to work on a couple of systems while still being UB and therefore not working on other systems.

Consider the following code snippet:

unsigned long long int a = std::numeric_limits< unsigned long long int >::max();
unsigned int b           = 12;

bool test = a > b;

My question is: Can we compare integers of different size with one another without explicitly casting the smaller type to the bigger one using e.g. static_cast without running into undefined behavior (UB)?

In general there are three ways I can imagine this turning out:

  1. The smaller type is implicitly cast to the bigger type before conversion (either via a real cast or by some clever way of being able to "pretend" it had been casted)
  2. The bigger type is truncated to the size of the smaller one before comparison
  3. This is not defined and one needs to add in an explicit cast in order to arrive at defined behavior
Raven
  • 2,951
  • 2
  • 26
  • 42
  • Not UB. Google for "C++ integer promotion" – selbie Apr 07 '21 at 17:56
  • 1
    This falls under the rules for [integer promotion](https://en.cppreference.com/w/c/language/conversion#Integer_promotions). No UB. – 0x5453 Apr 07 '21 at 17:56
  • See also the section on which conversions are applied for [arithmetic operators](https://en.cppreference.com/w/cpp/language/operator_arithmetic) – Nathan Pierson Apr 07 '21 at 17:57
  • As @NathanPierson answer says your example will work but be careful if you mix signed and unsigned (simple advice is don't do it). – Richard Critten Apr 07 '21 at 17:59
  • 2
    Tip: If applicable (as it is here), [you can check](http://eel.is/c++draft/expr.const) if something is UB by running it in a `constexpr` function at compile-time (making sure to actually force a compile-time result) and seeing whether it produces a compiler error. By applicable, I mean the code being _allowed_ to run at compile-time (so no exceptions, no `asm`, no `reinterpret_cast`, etc.) [Here's a simple example testing the dereference of a null pointer.](https://gcc.godbolt.org/z/3n1fabTYf) – chris Apr 07 '21 at 18:17
  • @chris ah yes this is indeed true. Thanks for pointing that out. I will use this trick in the future! – Raven Apr 08 '21 at 07:29

2 Answers2

3

This is not undefined behavior. This is covered by the usual arithmetic conversions which are detailed in section 8p11.5 of the C++17 standard:

The integral promotions (7.6) shall be performed on both operands. Then the following rules shall be applied to the promoted operands:

  • (11.5.1) If both operands have the same type, no further conversion is needed.
  • (11.5.2) Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank shall be converted to the type of the operand with greater rank.
  • (11.5.3) Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.
  • (11.5.4)Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.
  • (11.5.5)Otherwise, both operands shall be converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

The passage in bold is what applies here. Since both types are unsigned, the the smaller type is converted to the larger type as the format can hold a subset of values the latter can hold.

dbush
  • 205,898
  • 23
  • 218
  • 273
1

This is safe. C++ has what are called the Usual arithmetic conversions and they handle how to implicitly convert the objects passed to the built in binary operators.

In this case, integer promotion happens and b is converted to a unsigned long long int for you and then operator > is evaluated.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402