4

In a C implementation where int can hold all values of type unsigned short, standard integer promotion rules dictate that given the code:

unsigned short x=(USHORT_MAX+1)-3; // Or just -3
x*=x;

a compiler should evaluate the latter statement as equivalent to x=(unsigned short)((int)x * (int)x);. Even though unsigned values are nominally supposed to behave according to the rules of modular arithmetic, the standard imposes no requirements upon the behavior of such a statement when x exceeds sqrt(INT_MAX), and a compiler could legitimately specify a behavior other than computing a result as if the computation were performed in unsigned fashion in the destination type. Has any historical C implementation ever specified, in cases where the result of integer operators other than >>, %, and / is coerced to an unsigned type smaller than int, a behavior contrary to what would be yielded if the operands were coerced to, and computation were performed in, the destination type?

On a related note, how many "classical" C compilers have there been which, given code like the above, might plausibly have failed to compute 9? A two's-complement machine would get that result if it simply ignored any overflow in the multiply, but a sign-magnitude machine which attempted to perform the computation using signed integer math could not. On the other hand, having compilers look at how an expression will be used when deciding how to evaluate it has been a longstanding practice in many C compilers, and a compiler could easily observe that if arithmetic is being performed on non-negative values and the result is being coerced to unsigned short, the standard would allow unsigned values to be substituted, avoiding any need for sign-handling logic.

22
  • Did this actually happen to you, or is it merely speculative? If it's defined behavior in the C specification, and the compiler ever produces some result other than the one you illustrated, then the compiler is broken. That said, if you're referring to math in the general case, the specification does allow some variation, depending on the specific definition of the data types in the compiler implementation. May 1, 2015 at 20:30
  • 2
    @RobertHarvey: If int is 32 bits and unsigned short is 16 bits, then because of integer promotion rules the behavior is undefined. The vast majority of compilers will in fact perform the computation according to the rules of modular arithmetic, but some hyper-modern compilers would instead treat code like x *= x; as an invitation to infer that in any circumstance where it is reachable, x must be less than 46341, and that if the code isn't reachable with x less than 46341 it must not be reachable at all. Such assumptions can severely break code that would otherwise work just fine.
    – supercat
    May 1, 2015 at 20:41
  • 1
    @RobertHarvey: The compiler making such an inference would be conforming to the spec as written. Personally, I think the problem is a broken spec which could and should be fixed by saying the result in such cases must be as if computed with an unsigned type (yielding completely-defined behavior) but it's possible that code might exist somewhere that quite legitimately relies upon a compiler doing something unusual in the aforementioned cases, and specifying the common behavior might forbid a compiler from processing such code in the fashion it requires.
    – supercat
    May 1, 2015 at 20:49
  • 1
    There are ways to write code like this that are platform independent and avoid these problems; the spec can already adequately encompass those ways. The way to do that would be to write your code in such a way that there's no possibility of promotion ever occurring; one obvious way would be to simply avoid the smaller int sizes. May 1, 2015 at 21:02
  • 1
    Then don't use small unsigned types. May 1, 2015 at 21:20

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.