On 7/30/2012 2:26 PM, Ronald Landheer-Cieslak wrote:

> Eric Sosman <(E-Mail Removed)> wrote:

>> On 7/30/2012 9:53 AM, Ronald Landheer-Cieslak wrote:

>>> Barry Schwarz <(E-Mail Removed)> wrote:

>>>> [...]

>>>> While the contributors to this group may not be able to infer why the

>>>> designers chose what they did does not imply the absence of a rationale.

>>>> For example, while they are the same size, unsigned int and unsigned long

>>>> have different conversion ranks. This may make a difference in the

>>>> generated code and may have driven the compiler writers to choose one over the other.

>>>

>>> Excuse my ignorance, but what's a "conversion rank"?

>>

>> Shorthand for "integer conversion rank," of course.

>>

>> C sometimes needs to convert values from one type to another

>> before working with them. For example, you cannot compare an

>> `unsigned short' and a `signed int' as they stand; you must first

>> convert them to a common type and then compare the converted values.

>> But what type should be chosen? Plausible arguments could be made

>> for any of `unsigned short' or `signed int' or `unsigned int' or

>> even `unsigned long', depending on the relative "sizes" of these

>> types on the machine at hand.

>>

>> "Integer conversion rank" formalizes this notion of "size."

>> In the old days there were only a few integer types and it was

>> easy to enumerate the possible combinations. Things got more

>> complicated when C99 not only introduced new integer types, but

>> made the set open-ended: An implementation might support types

>> like `int24_t' or `uint_least36_t', and we need to know where

>> these fit with respect to each other and to generic types like

>> `long'. For example, when you divide a `uint_least36_t' by a

>> `long', what conversions occur? Inquiring masochists want to know.

>>

>> To this end, each integer type has an "integer conversion rank"

>> that establishes a pecking order. Roughly speaking, "narrow" types

>> have low ranks and "wide" types have higher ranks. It's all in

>> section 6.3.1.1 of the Standard, which takes quite a bit of prose

>> to express this "narrow versus wide" idea precisely -- but that's

>> really all it's doing: narrow versus wide, and how to handle ties.

>>

>> Eventually, when C needs to perform "integer promotions" or

>> "usual arithmetic conversions," its choice of target type for

>> integers is driven by the integer conversion rank(s) of the original

>> type(s) involved.

>

> OK, so it basically formalizes the conversions that the integer types goes

> through to end up with either something useful or something

> implementation-defined, or both.

>

> Reading the draft Barry pointed to, it doesn't seem to actually change any

> of the rules as they were before - just formalize them, is that right?
Pretty much, yes: It's formalized to make it work with

implementation-defined integer types the Standard doesn't know

about, or doesn't require, or doesn't fully specify.

> (and

> comparing a negative signed short to an unsigned long still yields

> implementation-defined results).
Within limits, yes. Let's work through it:

- First, we consult 6.5.8 for the relational operators, and

learn that the "usual arithmetic conversions" apply to

both operands.

- Over to 6.3.1.8 for the UAC's, where we learn that the

"integer promotions" are performed on each operand,

independently.

- 6.3.1.1 describes the IP's. We find that `unsigned long'

is unaffected. It takes a little more research, but we

eventually find that `signed short' converts to `int'.

- 6.3.1.3 tells us that this conversion preserves the

original value, so we now have the `int' whose value

is the same as that of the original `signed short'.

- Back to 6.3.1.8 again to continue with the UAC's, now with

an `unsigned long' and an `int' and working through the

second level of "otherwise." There we find that we've got

one signed and one unsigned operand, *and* the unsigned

operand has the higher rank (consult 6.3.1.1 again). This

tells us we must convert the `int' again, this time to

`unsigned long'.

- Over to 6.3.1.3 again for the details of the conversion,

and if the `int' is negative we must use "the maximum

value that can be represented in the new type" to finish

converting. ULONG_MAX is an implementation-defined value,

so this is where implementation-definedness creeps in.

- ... and we're back to 6.5.8, with two `unsigned long' values,

which we know how to compare.

Seems like quite a lot of running around for a "simple" matter,

but consider: Before the first ANSI Standard nailed things down,

different C implementations disagreed on how some comparisons should

be done! Both the "unsigned preserving" and "value preserving" camps

(see the Rationale) would have agreed on the particular example we've

just worked through, but would have produced different results for

some other comparisons. The Standard's complicated formalisms --

including "integer conversion rank" -- are part of an attempt to

eliminate or at least minimize such disagreements.

--

Eric Sosman

(E-Mail Removed)d