How does the computer convert between differently sized signed intgers?
For example when i convert the long long int value 12000000000 to int how does it reduce the value? And how does it handle negative numbers?
how does it reduce the value?
From C11 standard 6.3.1.3p3:
When a value with integer type is converted to another integer 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.
It is not defined how to convert the value - instead, each compiler may have different behavior, but it has to have some documented behavior. Nowadays we live in twos-complement world - it's everywhere the same. Let's take a look at gcc compiler - from ex. gcc documentation integers implementation-defined behavior:
The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).
For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.
So we have:
long long int value 12000000000 to int
Let's assume long long int has 64 bits and int has 32 bits and byte has 8 bits and we use twos-complement, so INT_MIN = -2147483648 INT_MAX = 2147483647 and N is 32, and 2^N is, well, 4294967296. You should take a peek at modular arithmetic and we know that:
12000000000 = 3 * 4294967296 + -884901888
So it will be converted to -884901888. That is irrelevant to what format is used to store the number - it can be in any format it wishes.
Now, gcc is smart, and while the documentation states the mathematical description of the algorithm in modulo arithmetic, you can note that:
$ printf("%16llx\n%16x\n", 12000000000ll, (int)12000000000ll);
2cb417800
cb417800
Ie the mathematical operation of "modulo 2^32" is equal in binary to doing an AND mask with all bits set num & 0xffffffff.
And how does it handle negative numbers?
Exactly the same way, there's just a minus. For example -12000000000ll :
-12000000000ll = -3 * 4294967296 + 884901888
So (int)-12000000000ll will be converted to 884901888. Note that in binary it's just:
$ printf("%16llx\n%16x\n", -12000000000ll, (int)-12000000000ll);'
fffffffd34be8800
34be8800
Attempting to convert an integer representation to a smaller, signed type in which it cannot be properly represented (such as your example of trying to convert 12000000000 to a 32-bit int) is implementation-defined behaviour. From this C11 Draft Standard (the third paragraph being relevant here):
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.60)
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
I found a solution, that works with 2s complement. It can convert integers up and down in width and works with positive and negative numbers.
a is the number,
oldp is the old position of the sign bit
newp is the new position of the sign bit
uint64_t shsbc(uint64_t a, uint32_t oldp, uint32_t newp) {
if (!(a>>oldp&1)) {
if (oldp > newp) {
return(a & UINT64_MAX>>(64 - (newp + 1)));
} else {
return(a & UINT64_MAX>>(64 - (oldp + 1)));
}
}
if (oldp > newp) {
a &= UINT64_MAX>>((oldp - newp) + 1 + (63 - oldp));
a |= ((uint64_t) 1)<<newp;;
return(a);
} else {
a &= UINT64_MAX>>((newp - oldp) + 1 + (63 - newp));
a |= UINT64_MAX>>(63 - (newp - oldp))<<(newp - oldp - 1);
return(a);
}
}
Related
Is the following guaranteed to work or implementation defined?
unsigned int a = 4294967294;
signed int b = a;
The value of b is -2 on gcc.
From C99
(§6.3.1.3/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.
The conversion of a value to signed int is implementation-defined (as you correctly mentioned because of 6.3.1.3p3) . On some systems for example it can be INT_MAX (saturating conversion).
For gcc the implementation behavior is defined here:
The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 6.3.1.3).
For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.
http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html
#ouah's answer tells you that its implementation-defined, but doesn't explain how your implementation yields (-2) specifically. I'll answer that:
1) Your implementation seems to have 32-bit wide int type and 2's complement representation.
2) 4294967294 is (UINT_MAX - 1) = 0xfffffffe.
In your implementation, (UINT_MAX - 1) is converted to signed int as follows:
0xfffffffe is converted as ~(0xfffffffe) + 1 = (1 in binary) + 1 = 10 in binary = 2 in decimal.
Notice that before this conversion the most significant bit was 1 (in 0xfffffffe), so the final number is interpreted to be a negative number after the aforementioned conversion. Thus, you get (-2) as your final answer after the conversion.
Hope this helped.
I want the following idea: I have short a = -4; and I have an unsigned short b = (unsigned short)a;
When I printf("%hu", b), why doesn't it print 4? How can I convert a negative integer to a positive integer using casting?
short and unsigned short are normally 16 bit integers. So limits are -32768 to 32767 for the signed version (short) and 0 to 65535 for unsigned short. Casting from signed to unsigned just wraps values, so -4 will be casted to 65532.
This is the way casting to unsigned works in C language.
If you accept to use additions/substractions, you can do:
65536l - (unsigned short) a
The operation will use the long type (because of the l suffix) which is required to be at least a 32 bits integer type. That should successfully convert any negative short integer to its absolute value.
It sounds like you want the absolute value of the number, not a cast. In C we have the abs / labs / llabs functions for that, found in <stdlib.h>.
If you know ahead of time that the value is negative, you can also just negate it: -a.
If you were instead just looking to get the absolute value, you should have used the abs() function from <stdlib.h>.
Otherwise, when you make such a cast, you trigger the following conversion rule from C17 6.3.1.3:
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. 60)
Where foot note 60) is important:
The rules describe arithmetic on the mathematical value, not the value of a given type of expression.
Mathematical value meaning we shouldn't consider wrap-around etc when doing the calculation. And the signedness format (2's complement etc) doesn't matter either. Applying this rule, then:
You have mathematical value -4 and convert from signed short to unsigned short.
We add one more than the maximum value. That is -4 + USHRT_MAX + 1, where UINT_MAX is likely 2^16 = 65535.
-4 + 65535 + 1 = 65532. This is in range of the new type unsigned short.
We got in range at the first try, but "repeatedly adding or subtracting" is essentially the same as taking the value modulo (max + 1).
This conversion is well-defined and portable - you will get the same result on all systems where short is 16 bits.
A short int in C contains 16 bits and the first bit represents whether or not the value is negative or positive. I have a C program that is as follows:
int main() {
short int v;
unsigned short int uv;
v = -20000;
uv = v;
printf("\nuv = %hu\n", uv);
return 0;
}
Since the value of v is negative I know the first bit of the variable is a 1. So I expect the output of the program to equal uv = 52,768 b/c 20,000 + (2^15) = 52,768.
Instead I am getting uv = 45536 as the output. What part of my logic is incorrect?
The behavior you're seeing can be explained by the conversion rules of C:
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.
(This quote is from C99.)
-20000 can't be represented by an unsigned short because it's negative. The target type is unsigned, so the value is converted by repeatedly adding 65536 (which is USHORT_MAX + 1) until it is in range: -20000 + 65536 is exactly 45536.
Note that this behavior is mandated by the C standard and has nothing to do with how negative numbers are actually represented in memory (in particular, it works the same way even on machines using sign/magnitude or ones' complement).
In the first block of code both conditions hold TRUE. In second, the first holds true and the other holds false.
int8_t i8 = -2;
uint16_t ui16 = i8;
if(ui16 == -2) //this is TRUE
if(ui16 == 65534) //this is TRUE as well
And This is the second scenario:
int8_t i8 = -2;
int16_t i16 = i8;
if(i16 == -2) //this is TRUE
if(i16 == 65534) //this is NOT TRUE !!!
Because -2 fits into int16_t whereas -2 is converted to unsigned in uint16_t.
This is well-defined behaviour.
from ISO/IEC 9899 (C99 standard working draft):
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.49)
...
49) The rules describe arithmetic on the mathematical value, not the value of a given type of expression
So if I do:
uint16_t i = -2;
the compiler should do:
i = -2 + (USHRT_MAX + 1);
or
i = -2 - (USHRT_MAX + 1);
until we get a value storable within 16 bits with no sign bit.
Not dependent on the rank of -2, but the mathematical value.
In your case this should be: 65534
Which it is with gcc.
[C++ follows the same rules for signed conversions]
In your second section of code you are simply assigning a lower rank value to a higher rank variable.
e.g. using more bits of precision to store the same number.
When you check against i16 == 65534 you are invoking this part of the standard from the same section:
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.
because 65534 is not storable in 15 bits and a sign bit (215 - 1).
So invoking implementation defined behaviour.
Relying on the return value of this is just as bad as relying on undefined behaviour unless you're a compiler developer.
In C, unsigned integers always behave according to modular (clock-face) arithmetic, but signed integers only sometimes, unreliably do.
Generally speaking, expecting one number to equal a different number is nonsense. You shouldn't write programs that way. If you want a number like -2 to behave like a positive unsigned value, you should explicitly write a cast like (uint16_t) -2. Otherwise, there are many things that could go wrong.
I was reading John Regehr's blog on how he gives his students an assignment about saturating arithmetic. The interesting part is that the code has to compile as-is while using typedefs to specify different integer types, see the following excerpt of the full header:
typedef signed int mysint;
//typedef signed long int mysint;
mysint sat_signed_add (mysint, mysint);
mysint sat_signed_sub (mysint, mysint);
The corresponding unsigned version is simple to implement (although I'm actually not sure if padding bits wouldn't make that problematic too), but I actually don't see how I can get the maximum (or minimum) value of an unknown signed type in C, without using macros for MAX_ und MIN_ or causing undefined behavior.
Am I missing something here or is the assignment just flawed (or more likely I'm missing some crucial information he gave his students)?
I don't see any way to do this without making assumptions or invoking implementation-defined (not necessarily undefined) behavior. If you assume that there are no padding bits in the representation of mysint or of uintmax_t, however, then you can compute the maximum value like this:
mysint mysint_max = (mysint)
((~(uintmax_t)0) >> (1 + CHAR_BITS * (sizeof(uintmax_t) - sizeof(mysint))));
The minimum value is then either -mysint_max (sign/magnitude or ones' complement) or -mysint_max - 1 (two's complement), but it is a bit tricky to determine which. You don't know a priori which bit is the sign bit, and there are possible trap representations that differ for different representations styles. You also must be careful about evaluating expressions, because of the possibility of "the usual arithmetic conversions" converting values to a type whose representation has different properties than those of the one you are trying to probe.
Nevertheless, you can distinguish the type of negative-value representation by computing the bitwise negation of the mysint representation of -1. For two's complement the mysint value of the result is 0, for ones' complement it is 1, and for sign/magnitude it is mysint_max - 1.
If you add the assumption that all signed integer types have the same kind of negative-value representation then you can simply perform such a test using an ordinary expression on default int literals. You don't need to make that assumption, however. Instead, you can perform the operation directly on the type representation's bit pattern, via a union:
union mysint_bits {
mysint i;
unsigned char bits[sizeof(mysint)];
} msib;
int counter = 0;
for (msib.i = -1; counter < sizeof(mysint); counter += 1) {
msib.bits[counter] = ~msib.bits[counter];
}
As long as the initial assumption holds (that there are no padding bits in the representation of type mysint) msib.i must then be a valid representation of the desired result.
I don't see a way to determine the largest and smallest representable values for an unknown signed integer type in C, without knowing something more. (In C++, you have std::numeric_limits available, so it is trivial.)
The largest representable value for an unsigned integer type is (myuint)(-1). That is guaranteed to work independent of padding bits, because (§ 6.3.1.3/1-2):
When a value with integer type is converted to another integer type… 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.
So to convert -1 to an unsigned type, you add one more than the maximum representable value to it, and that result must be the maximum representable value. (The standard makes it clear that the meaning of "repeatedly adding or subtracting" is mathematical.)
Now, if you knew that the number of padding bits in the signed type was the same as the number of padding bits in the unsigned type [but see below], you could compute the largest representable signed value from the largest representable unsigned value:
(mysint)( (myuint)(-1) / (myuint)2 )
Unfortunately, that's not enough to compute the minimum representable signed value, because the standard permits the minimum to be either one less than the negative of the maximum (2's-complement representation) or exactly the negative of the maximum (1's-complement or sign/magnitude representations).
Moreover, the standard does not actually guarantee that the number of padding bits in the signed type is the same as the number of padding bits in the unsigned type. All it guarantees is that the number of value bits in the signed type be no greater than the number of value bits in the unsigned type. In particular, it would be legal for the unsigned type to have one more padding bit than the corresponding signed type, in which case they would have the same number of value bits and the maximum representable values would be the same. [Note: a value bit is neither a padding bit nor the sign bit.]
In short, if you knew (for example by being told) that the architecture were 2's-complement and that corresponding signed and unsigned types had the same number of padding bits, then you could certainly compute both signed min and max:
myuint max_myuint = (myuint)(-1);
mysint max_mysint = (mysint)(max_myuint / (my_uint)2);
mysint min_mysint = (-max_mysint) - (mysint)1;
Finally, casting an out-of-range unsigned integer to a signed integer is not undefined behaviour, although most other signed overflows are. The conversion, as indicated by §6.3.1.3/3, is implementation-defined behaviour:
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.
Implementation-defined behaviour is required to be documented by the implementation. So, suppose we knew that the implementation was gcc. Then we could examine the gcc documentation, where we would read the following, in the section "C Implementation-defined behaviour":
Whether signed integer types are represented using sign and
magnitude, two's complement, or one's complement, and whether the
extraordinary value is a trap representation or an ordinary value
(C99 6.2.6.2).
GCC supports only two's complement integer types, and all bit
patterns are ordinary values.
The result of, or the signal raised by, converting an integer to a
signed integer type when the value cannot be represented in an
object of that type (C90 6.2.1.2, C99 6.3.1.3).
For conversion to a type of width N, the value is reduced modulo
2^N to be within range of the type; no signal is raised.
Knowing that signed integers are 2s-complement and that unsigned to signed conversions will not trap, but will produce the expected pattern of low-order bits, we can find the maximum and minimum values for any signed type starting with the maximum representable value for the widest unsigned type, uintmax_t:
uintmax_t umax = (uintmax_t)(-1);
while ( (mysint)(umax) < 0 ) umax >>= 1;
mysint max_mysint = (mysint)(umax);
mysint min_mysint = (-max_mysint) - (mysint)1;
This is a suggestion for getting the MAX value of a specific type set with typedef without using any library
typedef signed int mysint;
mysint size; // will give the size of the type
size=sizeof(mysint)*(mysint)8-(mysint)1; // as it is signed so a bit
// will be deleted for sign bit
mysint max=1;//start with first bit
while(--size)
{
mysint temp;
temp=(max<<(mysint)1)|(mysint)1;// set all bit to 1
max=temp;
}
/// max will contain the max value of the type mysint
If you assume eight-bit chars and a two's complement representation (both reasonable on all modern hardware, with the exception of some embedded DSP stuff), then you just need to form an unsigned integer (use uintmax_t to make sure it's big enough) with sizeof(mysint)*8 - 1 1's in the bottom bits, then cast it to mysint. For the minimum value, negate the maximum value and subtract one.
If you don't want to assume those things, then it's still possible, but you'll need to do some more digging through limits.h to compensate for the size of chars and the sign representation.
I guess this should work irrespective of negative number representation
// MSB is 1 and rests are zero is minimum number in both 2's and 1's
// compliments representations.
mysint min = (1 << (sizeof(mysint) * 8 - 1));
mysint max = ~x;