简体   繁体   中英

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?

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 2 N , 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.

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.

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 2 N , where N is the width of the destination type.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM