overflow when multiplying unsigned chars? - c

When I multiply two unsigned chars in C like this:
unsigned char a = 200;
unsigned char b = 200;
unsigned char c = a * b;
Then I know I will have an overflow, and I get (40'000 modulo 256) as a result. When I do this:
unsigned char a = 200;
unsigned char b = 200;
unsigned int c = (int)a * (int)b;
I will get the correct result 40'000. However, I do not know what happens with this:
unsigned char a = 200;
unsigned char b = 200;
unsigned int c = a * b;
Can I be sure the right thing happens? Is this compiler dependent? Similarly, I don't know what happens when doing a subtraction:
unsigned char a = 1;
unsigned char b = 2;
int c = a - b;
When making "c" an unsigned char, I will probably get 255 as a result. What happens when I use an int like this?

Argument of arithmetic operators get the "usual arithmetic promotions".
In cases where int can represent all the values of all the operands (at it is the case for your example in most implementations), arguments are first converted to int. So in both cases, you get the correct result.

Copied from this answer In a C expression where unsigned int and signed int are present, which type will be promoted to what type?
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
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.
3 Otherwise, the new type is signed and the value cannot be represented in it; either the >result is implementation-defined or an implementation-defined signal is raised.

Related

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.

Byte to signed int in C

I have a binary value stored in a char in C, I want transform this byte into signed int in C.
Currently I have something like this:
char a = 0xff;
int b = a;
printf("value of b: %d\n", b);
The result in standard output will be "255", the desired output is "-1".
According to the C99 standard,
6.3.1.3 Signed and unsigned integers
When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
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.
Otherwise, the new type is signed and the value cannot be represented in it; either the
result is implementation-defined or an implementation-defined signal is raised.
You need to cast your char to a signed char before assigning to int, as any value char could take is directly representable as an int.
#include <stdio.h>
int main(void) {
char a = 0xff;
int b = (signed char) a;
printf("value of b: %d\n", b);
return 0;
}
Quickly testing shows it works here:
C:\dev\scrap>gcc -std=c99 -oprint-b print-b.c
C:\dev\scrap>print-b
value of b: -1
Be wary that char is undefined by the C99 standard as to whether it is treated signed or unsigned.
6.2.5 Types
An object declared as type char is large enough to store any member of the basic
execution character set. If a member of the basic execution character set is stored in a
char object, its value is guaranteed to be positive. If any other character is stored in a char object, the resulting value is implementation-defined but shall be within the range of values that can be represented in that type.
...
The three types char, signed char, and unsigned char are collectively called
the character types. The implementation shall define char to have the same range,
representation, and behavior as either signed char or unsigned char.
Replace:
char a = 0xff
by
signed char a = 0xff; // or more explicit: = -1
to have printf prints -1.
If you don't want to change the type of a, as #veer added in the comments you can simply cast a to (signed char) before assigning its value to b.
Note that in both cases, this integer conversion is implementation-defined but this is the commonly seen implementation-defined behavior.
You are already wrong from the start:
char a = 0xff;
if char is signed, which you seem to assume, here you already have a value that is out of range, 0xFF is an unsigned quantity with value 255. If you want to see char as signed numbers use signed char and assign -1 to it. If you want to see it as a bit pattern use unsigned char and assign 0xFF to it. Your initialization of the int will then do what you expect it to do.
char, signed char and unsigned char are by definition of the standard three different types. Reserve char itself to characters, printing human readable stuff.

Why is an unsigned int 1 lower than a char y -1?

main() {
unsigned x = 1;
char y = -1;
if (x > y)
printf("x>y");
else
printf("x<=y");
}
I expected x>y,
but I had to change unsigned int to signed int to get the expected result.
If char is equivalent to signed char:
char is promoted to int (Integer Promotions, ISO C99 §6.3.1.1 ¶2)
Since int and unsigned int have the same rank, int is converted to unsigned int (Arithmetic Conversions, ISO C99 §6.3.1.8)
If char is equivalent to unsigned char:
char may be promoted to either int or unsigned int:
If int can represent all unsigned char values (typically because sizeof(int) > sizeof(char)), char is converted to int.
Otherwise (typically because sizeof(char)==sizeof(int)), char is converted to unsigned.
Now we have one operand that is either int or unsigned int, and another that is unsigned int. The first operand is converted to unsigned int.
Integer promotions:
An expression of a type of lower rank that int is converted to int if int can hold all of the values of the original type, to unsigned int otherwise.
Arithmetic conversions:
Try to convert to the larger type. When there is conflict between signed and unsigned, if the larger (including the case where the two types have the same rank) type is unsigned, go with unsigned. Otherwise, go with signed only in the case it can represent all the values of both types.
Conversions to integer types(ISO C99 §6.3.1.3):
Conversion of an out-of-range value to an unsigned integer type is done via wrap-around (modular arithmetic).
Conversion of an out-of-range value to a signed integer type is implementation defined, and can raise a signal (such as SIGFPE).
When using signed and unsigned in single operation the signed got promoted to unsigned by C's automatic type conversion. If the bit patter of -1 is considered an unsigned number then it is a very very high value. So x > y is false.

Resources