Search code examples
c++typescastingarithmetic-expressions

What is the casting order of operations in arithmetics in c++?


Why does

  1. result = static_cast<double>(1 / (i+1))

return int in C++ and why does

  1. result = 1 / (i+static_cast<double>(1))

return double? Specifically why is casting after the +-operation sufficient to produce a double. Why is it not required before the + or in the numerator as well? Is static_cast the preferred way of casting?

Code:

double harmonic(int n) {
  double result = 0;
  for (int i = 0; i < n; i++) {
    result += 1 / static_cast<double>(i+1);
  }
  return result;
}

Solution

  • There's no such thing as "casting order" because the type of an expression depends on its operands. Put it simply, if a binary arithmetic operator accepts two operands of different types then the smaller type will be implicitly converted to the wider type

    In result = static_cast<double>(1 / (i+1)) it's parsed like this

    • i + 1 is an int expression since both i and 1 are of type int
    • 1 / (i + 1) returns int for the same reason
    • Then the result of 1 / (i + 1), which is an int, is statically cast to double

    OTOH in result = 1 / (i+static_cast<double>(1)) it's like this

    • 1 is cast to double
    • i + static_cast<double>(1) returns double because i is upconverted to double due to the higher rank of the other operand
    • 1 / (i+static_cast<double>(1)) is a double expression for the same reason. It's also a floating-point division instead of an integer division like in the other case

    But don't cast like that. It's better to do 1 / (i + 1.0) or 1.0 / (i + 1) instead

    The complete rule is like this

    • If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
    • Otherwise, if either operand is long double, the other operand is converted to long double
    • Otherwise, if either operand is double, the other operand is converted to double
    • Otherwise, if either operand is float, the other operand is converted to float
    • Otherwise, the operand has integer type (because bool, char, char8_t, char16_t, char32_t, wchar_t, and unscoped enumeration were promoted at this point) and integral conversions are applied to produce the common type, as follows:
      • If both operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the operand with the greater integer conversion rank
      • Otherwise, if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type.
      • Otherwise, if the signed operand's type can represent all values of the unsigned operand, the unsigned operand is converted to the signed operand's type
      • Otherwise, both operands are converted to the unsigned counterpart of the signed operand's type.

    The conversion rank above increases in order bool, signed char, short, int, long, long long. The rank of any unsigned type is equal to the rank of the corresponding signed type. The rank of char is equal to the rank of signed char and unsigned char. The ranks of char8_t, char16_t, char32_t, and wchar_t are equal to the ranks of their underlying types.

    Arithmetic operators