8051 16 bit addition/multiplication result 16 bit instead of 32 bit - c

I have a problem. In this program the variable x should be set to 0x10000 but in both operations the result is 0.
This is not the main program but a test to find the reason for the error. I am currently making a 64 bit multiplier with hex input. I used 16-bit multiplication using Keil and Proteus
int main() {
unsigned long int x = 0;
x = 0x8000 * 0x2;
x = 0x8000 + 0x8000;
return 0;
}

The literal 0x8000 is of type unsigned int. On your 16-bit machine, the int and therefore unsigned int is of the natural size of 16 bits, the smallest accepted by the C standard. The integer promotion rules say that the smaller width types are widened to int or unsigned int, but no further (C11 n1570 6.3.1.1p2):
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. 58) All other types are unchanged by the integer promotions.
An operand is widened from int or unsigned int only if the other operand has a greater rank.
Here, 0x8000 + 0x8000 is calculated using unsigned int which will wrap to 0, because the maximum value that can be represented in unsigned int is 0xFFFF.
You should force at least one of the operands to unsigned long using either the suffix UL, or by adding an explicit cast:
int main() {
unsigned long int x=0;
/* unsigned long int * int */
x = 0x8000UL * 0x2;
/* unsigned long + unsigned int */
x = (unsigned long)0x8000 + 0x8000;
return 0;
}
See also In a C expression where unsigned int and signed int are present, which type will be promoted to what type? for general discussion.

It is not clear way (or that) the variable x should be 0x10000. x is a unsigned long int, but the values you assign to it are unsigned int. And if on that platform int is only 16 bit, both 0x8000 * 2 and 0x8000 + 0x8000 are 0.
Try using 0x8000L (or better 0x8000UL) for creating long literals at the first place.

Related

Converting non-Ascii characters to int in C, the extra bits are supplemented by 1 rather than 0

When coding in C, I have accidently found that as for non-Ascii characters, after they are converted from char (1 byte) to int (4 bytes), the extra bits (3 bytes) are supplemented by 1 rather than 0. (As for Ascii characters, the extra bits are supplemented by 0.) For example:
char c[] = "ā";
int i = c[0];
printf("%x\n", i);
And the result is ffffffc4, rather than c4 itself. (The UTF-8 code for ā is \xc4\x81.)
Another related issue is that when performing right shift operations >> on a non-Ascii character, the extra bits on the left end are also supplemented by 1 rather than 0, even though the char variable is explicitly converted to unsigned int (for as for signed int, the extra bits are supplemented by 1 in my OS). For example:
char c[] = "ā";
unsigned int u_c;
int i = c[0];
unsigned int u_i = c[0];
c[0] = (unsigned int)c[0] >> 1;
u_c = (unsigned int)c[0] >> 1;
i = i >> 1;
u_i = u_i >> 1;
printf("c=%x\n", (unsigned int)c[0]); // result: ffffffe2. The same with the signed int i.
printf("u_c=%x\n", u_c); // result: 7fffffe2.
printf("i=%x\n", i); // result: ffffffe2.
printf("u_i=%x\n", u_i); // result: 7fffffe2.
Now I am confused with these results... Are they concerned with the data structures of char, int and unsigned int, or related to my operating system (ubuntu 14.04), or related to the ANSI C requirements? I have tried to compile this program with both gcc(4.8.4) and clang(3.4), but there is no difference.
Thank you so much!
It is implementation-defined whether char is signed or unsigned. On x86 computers, char is customarily a signed integer type; and on ARM it is customarily an unsigned integer type.
A signed integer will be sign-extended when converted to a larger signed type;
a signed integer converted to unsigned integer will use the modulo arithmetic to wrap the signed value into the range of the unsigned type as if by repeatedly adding or subtracting the maximum value of the unsigned type + 1.
The solution is to use/cast to unsigned char if you want the value to be portably zero-extended, or for storing small integers in range 0..255.
Likewise, if you want to store signed integers in range -127..127/128, use signed char.
Use char if the signedness doesn't matter - the implementation will probably have chosen the type that is the most efficient for the platform.
Likewise, for the assignment
unsigned int u_c; u_c = (uint8_t)c[0];,
Since -0x3c or -60 is not in the range of uint16_t, then the actual value is the value (mod UINT16_MAX + 1) that falls in the range of uint16_t; iow, we add or subtract UINT16_MAX + 1 (notice that the integer promotions could trick here so you might need casts if in C code) until the value is in the range. UINT16_MAX is naturally always 0xFFFFF; add 1 to it to get 0x10000. 0x10000 - 0x3C is 0xFFC4 that you saw. And then the uint16_t value is zero-extended to the uint32_t value.
Had you run this on a platform where char is unsigned, the result would have been 0xC4!
BTW in i = i >> 1;, i is a signed integer with a negative value; C11 says that the value is implementation-defined, so the actual behaviour can change from compiler to compiler. The GCC manuals state that
Signed >> acts on negative numbers by sign extension.
However a strictly-conforming program should not rely on this.

Data overflow while comparing the values

I have a doubt from the below 2 code snippets.
I ran this code on 64-bit machine (x86_64-linux-gnu). I can see the value Val overflows when the data type is unsigned integer.
#include<stdio.h>
main()
{
unsigned int Val = 0xFFFFFFFF-15, Val2 = 0xFFFFFFFF;
if (Val+16 < Val2)
{
printf("Val is less than Val2\n");
}
}
If the data type is unsigned char it does not overflow.
#include<stdio.h>
main()
{
unsigned char Val = 0xFF-15, Val2 = 0xFF;
if (Val+16 < Val2)
{
printf("Val is less than Val2\n");
}
}
I have two questions:
Does the value Val get promoted to high data type when the data type is unsigned char?
If yes, why did not it get promoted from 32-bit to 64-bit unsigned long?
The C11 standard says the following (C11 6.3.11p2.2):
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
Thus:
unsigned char will be promoted - however it is an implementation detail whether int can represent all values of unsigned char - so it might be promoted to an unsigned int on those platforms. Yours is not one of those platforms, thus your second comparison is (int)Val + 16 < (int)Val2.
as the last sentence of the quoted paragraph tells, an unsigned int is never promoted. Since the arithmetic is done on unsigned ints in the first fragment, the result of 0xFFFFFFFF - 15 + 16 is 0U on a computer with 32-value-bit unsigned int.
Yes, in the second case the numbers are promoted to int. If you modify your code thus:
#include<stdio.h>
int main()
{
unsigned char Val = 0xFF-15, Val2 = 0xFF;
if ((unsigned char)(Val+16) < Val2)
{
printf("Val is less than Val2\n");
}
}
You will get the behaviour you expect.

Cast implied unsigned integer type?

I'm new to C and have seen code such as (unsigned)b
Does that imply that b will be an unsigned int? Or what type does it imply?
b will be whatever type it was to begin with. That doesn't change
(unsigned)b will evaluate as whatever value b is, but that is subject to the cast to unsigned, which is synonymous with unsigned int.
How that happens depends entirely on the type of b to begin with, whether that type is convertible to unsigned int, and whether the value contained therein falls unscathed between 0...UINT_MAX or not.
Ex:
#include <stdio.h>
void foo(unsigned int x)
{
printf("%u\n", x);
}
int main()
{
int b = 100;
foo((unsigned)b); // will print 100
char c = 'a';
foo((unsigned)c); // will print 97 (assuming ASCII)
short s = -10; // will print 4294967286 for 32-bit int types
// (see below for why)
foo((unsigned)s);
// won't compile, not convertible
//struct X { int val; } x;
//foo((unsigned)x);
}
The only part of this that may raise your eye brow is the third example. When a value of a convertible type to an unsigned type is out of the unsigned type's range (ex: negative values are not same-value representable with any unsigned target types), the value is converted by repeatedly added the maximum value representable by the unsigned target plus-one to the out-of-range value until such time as it falls within the valid range of the unsigned type.
In other words, because -10 is not within the valid representation of unsigned int, UINT_MAX+1 is added to -10 repeatedly (only takes once in this case) until the result is within 0...UINT_MAX.
Hope that helps.
unsigned is a short of unsigned int
signed is a short of signed int
long is the short of long int
long long is the short of long long int
short is the short of short int

Is unsigned char always promoted to int?

Suppose the following:
unsigned char foo = 3;
unsigned char bar = 5;
unsigned int shmoo = foo + bar;
Are foo and bar values guaranteed to be promoted to int values for the evaluation of the expression foo + bar -- or are implementations allowed to promote them to unsigned int?
In section 6.2.5 paragraph 8:
For any two integer types with the same signedness and different integer conversion rank
(see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a
subrange of the values of the other type.
In section 6.2.5 paragraph 9:
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.
The guarantee that an integer type with smaller integer conversion rank has a range of values that is a subrange of the values of the other type seems dependent on the signedness of the integer type.
signed char corresponds to signed int
unsigned char corresponds to unsigned int
Does this mean that the value of an unsigned char is only guaranteed to be in the subrange of unsigned int and not necessarily int? If so, does that imply that an implementation could theoretically have an unsigned char value which is not in the subrange of an int?
are implementations allowed to promote them to unsigned int?
Implementations will promote to unsigned int if not all unsigned char values are representable in an int (as ruled by 6.2.5p9 in C99). See below for implementation examples.
If so, does that imply that an implementation could theoretically have an unsigned char value which is not in the subrange of an int?
Yes, example: DSP cpu with CHAR_BIT 16 or 32.
For example, TI C compiler for TMS320C55x: CHAR_BIT is 16 and UCHAR_MAX 65535, UINT_MAX 65535 but INT_MAX 32767.
http://focus.ti.com/lit/ug/spru281f/spru281f.pdf
I ran across this yesterday - hope that my answer is on topic.
uint8_t x = 10;
uint8_t y = 250;
if (x - y > 0) {
// never happens
}
if (x - y < 0U) {
// always happens
}
To my eyes at least it was appearing as though values x and y were being unexpectedly promoted, when in fact is was their result that was promoted.

Why is a negative int greater than unsigned int? [duplicate]

This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
Closed 4 years ago.
int main(void)
{
unsigned int y = 10;
int x = – 4;
if (x > y)
Printf("x is greater");
else
Printf("y is greater");
getch();
return (0);
}
Output: x is greater
I thought the output would be y is greater since it is unsigned. What's the reason behind this?
Because the int value is promoted to an unsigned int. specifically 0xFFFFFFFC on a 32-bit machine, which as an unsigned int is 4294967292, considerably larger than 10
C99 6.3.1.1-p2
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
To perform the conversion:
C99 6.3.1.3-p2
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
Which basically means "add UINT_MAX+1" (as I read it, anyway).
Regarding why the promotion was to the unsigned int side; precedence:
C99 6.3.1.8-p1
...Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Which tells me int vs. unsigned char should work as expected.
Test
int main()
{
int x = -4;
unsigned int y = 10;
unsigned char z = 10;
if (x > y)
printf("x>y\n");
else
printf("x<y\n");
if (x > z)
printf("x>z\n");
else
printf("x<z\n");
return 0;
}
Output
x>y
x<z
Well look at that.
A comparison between a signed and an unsigned value will be made in "unsigned space". I.e., the signed value will be converted to unsigned by adding UINT_MAX + 1. In implementation using the 2-complement for negative values, no special handling of the values is required under the hood.
In this example, the -4 is turned into a 0x100000000-4 = 0xFFFFFFFC which is clearly > 10.
When you compare two values in C, they both must be of the same type. In this case (int and unsigned int) the int value will be converted to an unsigned int first.
Second, unsigned integer arithmetic in C is done modulo the maximum value of that type + 1 (that is, it "loops around" so UINT_MAX + 1 is 0 again and vice versa). Therefore converting negative values to unsigned results in very large numbers.
The relevant section in the standard says:
6.3.1.3 Signed and unsigned integers
2
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or
subtracting one more than the maximum value that can be represented in the new type
until the value is in the range of the new type.
When you compare an int and an unsigned int the int is converted to unsigned int.
The convertion of an int to an unsigned int is done by adding UINT_MAX+1 (note that your int is negative). So actually you are comparing:
if (-3 + UINT_MAX > 10) //Since -4 is converted to UINT_MAX+1-4
Which is true.
The first bit of an int value is used to define if it's a positive or a negative one. (1 = negative, 0 positive)
Your both variable are cast into unsigned int before comparison where the 1 in the first bit will be interpreted as part of your number.
this code should work fine :
int main(void)
{
unsigned int y = 10;
int x = – 4;
if (x > (int) y)
Printf("x is greater");
else
Printf ("y is greater");
getch ( );
return (0);
}
int x=-4 (2's complement of 4 is 1111 1100 =252) and unsigned int y=10 is(0000 1010 =10) so 252 >10 so -4 is greater than 10.

Resources