Re: A portability poser: adding two unsigned shorts
- From: Eric Sosman <Eric.Sosman@xxxxxxx>
- Date: Mon, 11 May 2009 16:23:44 -0400
Mark Dickinson wrote:
Here's a simple, largely theoretical puzzle about portability and
standards compliance. I don't know of any existing implementations
where the problem described below could ever come up.
Suppose I want to write portable C99 code for a function that adds two
unsigned shorts x and y, and returns their sum modulo USHRT_MAX+1. On
the face of it, this seems trivial:
unsigned short add_ushorts(unsigned short x, unsigned short y) {
return x+y;
}
should do it. But I've just managed to convince myself that this code
isn't portable, and could even invoke undefined behaviour on some
platforms! And now I'm shocked and horrified, and would dearly like
someone to tell me that I'm misunderstanding.
The problem occurs if the width of int is exactly 1 larger than the
width of unsigned short (or equivalently, the precision of int is
equal to that of unsigned short). Then int is large enough (but only
just) to hold all unsigned short values, so according to C99 6.3.1.1
paragraph 2, both x and y are converted to int before the addition.
But because int is only just big enough to hold x and y, the sum x+y
could overflow, so the above function has the potential to invoke
undefined behaviour.
Question 1: Is this analysis correct, or am I missing something?
It's correct, as far as I can see. Note that "int is one bit
wider than unsigned short" implies padding bits in one or both of
the types (otherwise, they'd have to differ by a multiple of CHAR_BIT
bits).
Question 2: The above problem (if it is a problem) is easily fixed by
explicitly casting x and y to unsigned int before the addition. But
in the actual code I'm looking at, I don't have unsigned short, but
instead I have a type 'digit', which is a typedef for some unsigned
integer type of unknown rank. So I want:
digit add_digits(digit x, digit y) {
return x+y;
}
except that this still has the same portability problem as above.
What's the best way to write this so that it's fully
standards-compliant and portable, and will do the correct thing
(addition modulo one more than the largest value representable as a
digit) for any unsigned integer type 'digit'.
The problem can only occur if digit has a lesser rank than int;
anything with greater rank is unchanged by the integer promotions.
Also, any unsigned type of rank not exceeding int can convert safely
to unsigned int. Putting these together and trusting your compiler
to eliminate dead code leads to
digit add_digits(digit x, digit y) {
if ((digit)-1 <= (unsigned int)-1)
return (unsigned int)x + (unsigned int)y;
else
return x + y;
}
Aside: Did I hear rumors of a TC to C99 that forbade padding bits
in integer types? Or was I just dreaming a dream of the Blissful Realm?
--
Eric.Sosman@xxxxxxx
.
- Follow-Ups:
- Re: A portability poser: adding two unsigned shorts
- From: David Thompson
- Re: A portability poser: adding two unsigned shorts
- From: Mark Dickinson
- Re: A portability poser: adding two unsigned shorts
- References:
- A portability poser: adding two unsigned shorts
- From: Mark Dickinson
- A portability poser: adding two unsigned shorts
- Prev by Date: Re: Library functions
- Next by Date: Re: More Net Nanny
- Previous by thread: Re: A portability poser: adding two unsigned shorts
- Next by thread: Re: A portability poser: adding two unsigned shorts
- Index(es):
Relevant Pages
|