Seva Alekseyev
Seva Alekseyev

Reputation: 61378

Order of int32_t to uint64_t casting

Does the C++ standard guarantee whether integer conversion that both widens and casts away the sign will sign-extend or zero-extend?

The quick test:

int32_t s = -1;
uint64_t u = s;

produces an 0xFFFFFFFFFFFFFFFF under Xcode, but is that a defined behavior in the first place?

Upvotes: 4

Views: 1564

Answers (3)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136296

You can find the standard verbiage in other answers.

But to help you form an intuitive mental model of widening conversions, it is helpful to think of these as a 2-step process:

  1. Sign- or zero-extension of the value. If the value is of signed type, then sign extension is used. Here int32_t is sign-extended to int64_t. On x86, the signedness of type detetermines whether MOVSX or MOVZX instruction is used.
  2. Converting the extended value to the destination type (change of signedness). Here int64_t is converted to uint64_t. It involves 0 assembly instructions as registers are untyped, the compiler just treats that register, which contains the result of sign extension of int32_t, as uint64_t.

Note that the standard doesn't specify these steps, it just specifies the required result.

Upvotes: 2

NathanOliver
NathanOliver

Reputation: 180660

When you do

uint64_t u = s;

[dcl.init]/17.9 applies which states:

the initial value of the object being initialized is the (possibly converted) value of the initializer expression. A standard conversion sequence ([conv]) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered.

and if we look in [conv], under integral conversions, we have

Otherwise, the result is the unique value of the destination type that is congruent to the source integer modulo 2N, where N is the width of the destination type.

So what you are guaranteed to have happen is that -1 becomes the largest number possible to represent, -2 is one less then that, -3 is one less then -2 and so on, basically it "wraps around".


In fact,

unsigned_type some_name = -1;

Is the canonical way to create a variable with the maximum value for that unsigned integer type.

Upvotes: 4

Asteroids With Wings
Asteroids With Wings

Reputation: 17454

From the section on Integral conversions:

[conv.integral/3]: Otherwise, the result is the unique value of the destination type that is congruent to the source integer modulo 2N, where N is the width of the destination type.

In other words, the wrap-around "happens last".

Upvotes: 1

Related Questions