Reputation: 22705
#include<stdio.h>
int main(void)
{
unsigned short a,e,f ; // 2 bytes data type
unsigned int temp1,temp2,temp4; // 4 bytes data type
unsigned long temp3; // 8 bytes data type
a=0xFFFF;
e=((a*a)+(a*a))/(2*a); // Line 8
//e=(((unsigned long)(a*a)+(unsigned long)(a*a)))/(unsigned int)(2*a);
temp1=a*a;
temp2=a*a;
temp3=(unsigned long)temp1+(unsigned long)temp2; // Line 14
temp4=2*a;
f=temp3/temp4;
printf("%u,%u,%lu,%u,%u,%u,%u\n",temp1,temp2,temp3,temp4,e,f,a);
return(1);
}
How do I fix the arithmetic (At Line 8 by appropriate typecasting of intermediate results) so that overflows are taken care of ? Currently it prints 65534 instead of expected 65535.
Why is the typecast necessary for Line 14 ?
Upvotes: 0
Views: 216
Reputation: 320531
You have to promote the types before performing the overflowing operation. In line 8 that would be multiplication, so
e = ((unsigned) a * a + (unsigned) a * a) / (2 * (unsigned) a);
Note that promoting only one operand of such symmetrical operations as *
is enough. You can use (unsigned) a * (unsigned) a
if you wish, but (unsigned) a * a
will work as well.
This will take care of multiplication, which will no longer overflow. However, now the addition will overflow. While 32-bit unsigned
is enough for a * a
, it is not enough for a * a + a * a
. For that you'll need unsigned long
(assuming it is larger). You can formally promote the first operand of +
to unsigned long
e = ((unsigned long) ((unsigned) a * a) + (unsigned) a * a) / (2 * (unsigned) a);
(again, promoting only the first operand of +
is enough, meaning that the second multiplication can be left in unsigned
).
The above looks a bit too convoluted, and to make it look cleaner you can use unsigned long
in the first multiplication from the very beginning
e = ((unsigned long) a * a + (unsigned) a * a) / (2 * (unsigned) a);
or you can just use unsigned long
everywhere to make it look even cleaner
e = ((unsigned long) a * a + (unsigned long) a * a) / (2 * (unsigned long) a);
The very same problem appears in your temp1 = a * a;
lines. They will overflow for the very same reason. You have to do
temp1 = (unsigned) a * a;
temp2 = (unsigned) a * a;
to avoid overflow, i.e. promote a
before multiplication.
And that is exactly what you correctly do in line 14, i.e. promote operands of +
before addition, although promoting only one operand is perfectly sufficient
temp3 = (unsigned long) temp1 + temp2;
Upvotes: 2
Reputation: 183888
How do I fix the arithmetic (At Line 8 by appropriate typecasting of intermediate results) so that overflows are taken care of ?
You need to cast one partner of both multiplications in the numerator to a large enough type. Since (2^16-1)² = 2^32 - 2^17 + 1
, the numerator would overflow a 32-bit unsigned integer, hence you need something larger (typically that would be a 64-bit type). If (unsigned) long
meets the size requirement,
e = (((unsigned long)a*a)+((unsigned long)a*a))/(2*a);
would be well-defined and safe (also with a cast to long
, only 33 bits are needed here).
Using unsigned long
is however not portable, because it may be a 32-bit type. Use (unsigned) long long
or (u)int_least64_t
to be portable and safe.
Currently it prints 65534 instead of expected 65535.
That's because the result of the computation is -2
if int
is (as it very probably is) a two's complement 32-bit integer type with wrap-around behaviour on overflow [overflow of signed integers is undefined behaviour, though, so compiler optimisations could alter it; if the compiler analyses the computation and sees it's 2*a*a/(2*a)
it could simplify it to a
, since that would be the result for any non-zero a
where no overflow occurs; dividing by 0 is UB too, so all cases are covered].
(2^16-1)² = 2^32 - 2^17 + 1 ≡ -2^17 + 1 = -131071 (mod 2^32)
(-131071) + (-131071) = -262142
(-262142)/131070 = -2
The usual arithmetic conversions are applied to the operands of *
, +
and /
, so the unsigned short
operands are converted to int
(since here all unsigned short
values can be represented as int
s), and the arithmetic is performed at type int
.
The result -2
is then converted to unsigned short
by adding USHRT_MAX +1
to it, resulting in USHRT_MAX - 1
being the value of e
afterwards.
Why is the typecast necessary for Line 14 ?
because the addition of temp1
and temp2
would result in a value outside the 32-bit unsigned
range, so the reduction modulo 2^32
of that result would change the overall result.
Upvotes: 1
Reputation: 124692
You use "larger" types to begin with (i.e., don't declare a
as an unsigned short
, just declare it as an unsigned int
). If you need to cast then, well, just cast one side of an operation. The promotion will occur for you.
Upvotes: 0