Jean
Jean

Reputation: 22705

C Typecast: How to

#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

Answers (3)

AnT stands with Russia
AnT stands with Russia

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

Daniel Fischer
Daniel Fischer

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 ints), 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

Ed Swangren
Ed Swangren

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

Related Questions