Uses of a C++ Arithmetic Promotion Header

Uses of a C++ Arithmetic Promotion Header

This is definitely useful -- we use these sorts of things in the math library that I work on for correctly typing intermediate values in expressions. For example, you might have a templated addition operator:

template<typename Atype, typename Btype>
type_promote<Atype, Btype>::type operator+(Atype A, Btype B);

This way, you can write a generic operator that will handle different argument types, and it will return a value of the appropriate type to avoid precision loss in the expression that it appears in. It's also useful (in things like vector sums) for properly declaring internal variables within these operators.

As for the question of what ought to go with these: I just checked in our source code where we define them, and all we have there are just the simple ArithmeticPromotion declaration you describe -- three generic versions to resolve the complex-complex, complex-real, and real-complex variants using the specific real-real ones, and then a list of real-real ones -- about 50 lines of code in all. We don't have any other helper templates with them, and it doesn't (from our usage) look like there are any natural ones that we'd use.

(FWIW, if you don't want to write this yourself, download our source from http://www.codesourcery.com/vsiplplusplus/2.2/download.html, and pull out src/vsip/core/promote.hpp. That's even in the part of our library that's BSD-licensed, though it doesn't actually say so in the file itself.)

Why are integer types promoted during addition in C?

So it appears that the result of numberA + 1 was promoted to uint32_t

The operands of the addition were promoted to int before the addition took place, and the result of the addition is of the same type as the effective operands (int).

Indeed, if int is 32-bit wide on your compilation platform (meaning that the type that represents uint16_t has lower “conversion rank” than int), then numberA + 1 is computed as an int addition between 1 and a promoted numberA as part of the integer promotion rules, 6.3.1.1:2 in the C11 standard:

The following may be used in an expression wherever an int or unsigned int may be used: […] An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.

[…]

If an int can represent all values of the original type […], the value is converted to an int

In your case, unsigned short which is in all likelihood what uint16_t is defined as on your platform, has all its values representable as elements of int, so the unsigned short value numberA gets promoted to int when it occurs in an arithmetic operation.

Bitshift and integer promotion?

The so-called usual arithmetic conversions apply to many binary operators, but not all of them. For example they do not apply to the bit shift operators, &&, ||, comma operator, and assignment operators. This is the rule for the bit shift operators:

6.5.7 ... 3 Semantics ...
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

Why is (-1)%(unsigned int) different than (-1)%(int) +(what is it calculating with uint and is casting to int a good solution)

All of C's arithmetic operators (except << and >> which only do this with the first operand) promote both operands to a common type before performing the operation, according the the language's rules for type promotion. Promoting c (with value -1) to unsigned performs modular reduction, modulo one plus the max value representable in the type, i.e. one plus UINT_MAX. The -1 cancels out with the "one plus", and the result is UINT_MAX, typically 4294967295. Then 4294967295U % 10U is of course 5.

Result of type cast and bitwise operation in C depends on the order

First of all, none of your code is really reliable and won't do what you expect.

printf and all other variable argument length functions have a dysfunctional "feature" called the default argument promotions. This means that the actual type of the parameters passed undergo silent promotion. Small integer types (such as char and short) get promoted to int which is signed. (And float gets promoted to double.) Tl;dr: printf is a nuts function.

Therefore you can cast between various small integer types all you want, there will still be a promotion to int in the end. This is no problem if you use the correct format specifier for the intended type, but you don't, you use %d which is for int.

In addition, the ~ operator, like most operators in C, performs implicit integer promotion of its operand. See Implicit type promotion rules.


That being said, this line ~((~(unsigned short)0) >> 1) does the following:

  • Take the literal 0 which is of type int and convert to unsigned short.

  • Implicitly promote that unsigned short back to int through implicit integer promotion.

  • Calculate the bitwise complement of the int value 0. This is 0xFF...FF hex, -1 dec, assuming 2's complement.

  • Right shift this int by 1. Here you invoke implementation-defined behavior upon shifting a negative integer. C allows this to either result in a logical shift = shift in zeroes, or arithmetic shift = shift in a sign bit. Different result from compiler to compiler and non-portable.

    You get either 0x7F...FF in case of logical shift or 0xFF...FF in case of arithmetic shift. In this case it seems to be the latter, meaning you still have decimal -1 after shift.

  • You do bitwise complement of the 0xFF...FF = -1 and get 0.

  • You cast this to short. Still 0.

  • Default argument promotion convert it to int. Still 0.

  • %d expects a int and therefore prints accordingly. unsigned short is printed with %hu and short with %hd. Using the correct format specifier should undo the effect of default argument promotion.

Advice: study implicit type promotion and avoid using bitwise operators on operands that have signed type.

To simply display the lowest 2's complement value of various signed types, you have to do some trickery with unsigned types, since bitwise operations on their signed version are unreliable. Example:

int shift = sizeof(short)*8 - 1;  // 15 bits on sane systems
short s = (short) (1u << shift);
printf("%hd\n", s);

This shifts an unsigned int 1u 15 bits, then converts the result of that to short, in some "implementation-defined way", meaning on two's complement systems you'll end up converting 0x8000 to -32768.

Then give printf the right format specifier and you'll get the expected result from there.

C++ returning more precise of two template arguments from function?

There isn't any built-in support in the form of a library, but you can accomplish this using the conditional (?:) operator.

In reply to another answer, Johannes Schaub posted a promote<T, U> template that wraps the logic up quite nicely. With the template, you should be able to write:

template <typename A, typename B>  
vector< typename promote<A, B>::type >
operator+(const vector<A> &a, const vector<B> &b)
{
return vector< typename promote<A, B>::type >(a.x+b.x, a.y+b.y, a.z+b.z);
}

Arithmetic overflow warning when trying to allocate memory using new

This creates a size_t from an unsigned int, then multiplies the size_t with an unsigned int.

outcell = new Cell[size_t{ width / size } * (height / size)];

Depending on the use of width, height and size you might instead consider making these a size_t.

What will happen if I don't include header files

I know that I get warnings, but the programs runs perfectly.

That is an unfortunate legacy of pre-ANSI C: the language did not require function prototypes, so the standard C allows it to this day (usually, a warning can be produced to find functions called without a prototype).

When you call a function with no prototype, C compiler makes assumptions about the function being called:

  • Function's return type is assumed to be int
  • All parameters are assumed to be declared (i.e. no ... vararg stuff)
  • All parameters are assumed to be whatever you pass after default promotions, and so on.

If the function being called with no prototype fits these assumptions, your program will run correctly; otherwise, it's undefined behavior.

Adding this instance variable to the header of a C++11 file drives the compiler up the wall. Why?

It has a problem with you keeping a vector of non-const references

std::vector<H&> hs;

I would suggest maintaining either a vector of values or pointers, depending on if you want to own these H instances or just reference them.

std::vector<H> hs;
std::vector<H*> hs;


Related Topics



Leave a reply



Submit