The __ arithmetic data types in C , are__ the integer types , such as

`int`

or `unsigned long`

, and the floating point types , such as ` float `

, or ` long double`

.Table of Contents

## Conversion of the integer types

### What is widening ?

Widening __ only applies__ to the signed and unsigned integer types . It does not apply to other types , such as

`float`

or `double`

.Widening is not about converting from signed to unsigned , or from unsigned to signed , it is about expanding the signedness of an integer type , from a smaller type to a larger type , so from a smaller number of bits , to a larger one . The __ signedness of the integer type does not change__ .

__ For the signed type__ , widening is done by what is called :

`sign extension`

. If the value of the signed type is negative , it is extended by filling the new bits by the value `1`

, if the value of the signed type is nonnegative , than it is extended by filling the newly allocated bits , with `0`

.__ For the unsigned type__ , the newly allocated bits are filled with

`0`

.### What is truncation ?

Truncation __ happens only for__ the integer types , that have the same signedness , when passing from a larger integer type , to a smaller integer type , such as , when passing from

`int`

to `char`

, or from `unsigned int`

to `unsigned char`

.Be it is signed or unsigned , the larger type is __ made to fit the smaller type__ , by discarding the bits , of the larger type , that lies outside of the width of the smaller type , keeping only the lower order bits .

Truncation as described , which is keeping only a limited number of bits , __ doesn’t happen when converting __ from a floating point type , to an integer type , or between floating point types .

When converting from a floating type , to an integer type , the fractional part is discarded , the floating type bits are not made to fit the integer type width , but the floating type is transformed from a floating point representation , to an integer representation .

### The integer conversion procedure

Conversion of the integer types , __ consists of either __ performing widening or truncation first , and later reinterpreting the bits .

So first truncation or widening is performed . Truncation or widening

__of the type , the type is only made to fit , a larger or narrower width , of the same signedness .__

*does not change the signedness*After having performed widening or truncation , the gotten bits in the target width , are only

__to a new integer type , the target type .__

*reinterpreted , as belonging*The __ only exception to this rule__ , is when converting to the

`_Bool`

type , any nonzero value is converted to `1`

, and any zero value , is converted to `0`

.Having explained , how conversion happens for the integer types , let us explain the concept of integer `rank`

, __ before explaining when conversion to another integer type __ , takes place .

### What is a rank ?

__ Each integer type in C has a rank__ .

`unsigned`

, and `signed`

integer types of the __ same type , disregarding the signedness__ , have the same rank . For example

`int`

, and ` unsigned int`

, have the same rank .The __ order of the ranks__ of the integer types is , as follow :

_Bool < signed char < short < int < long < long long

The rank of any standard integer type , outlined above , is larger than the __ rank of any implementation defined__ , extended integer type , having the same width .

The __ char , and __ integer types , have the same rank .

`signed char`

The __ rank of an__ enum type is equal to its assigned implementation , defined integer type .

### Integer promotion

Integer types , which __ have a rank smaller__ than

`int`

, each is promoted , to either the `int`

type , if the `int `

type can represent all its possible values , or to the `unsigned int`

type , in the other case . Integer types having a rank smaller than `int`

, __ are promoted when__ an operator expects , that one of its operand , to be of an arithmetic type . Arithmetic types in C , are the integer or floating point types . An example of such an operator , is the subtraction operator

`-`

, or the unary negation operator `-`

.Integer promotion , is not how the integer is converted to another type , __ it is when __ a integer is converted to another type , in this case it is because it has a lower rank than

`int`

.unsigned char x = 1; signed char y = -x; /* x , is an unsigned char type , it has a rank lower than int . The unary negation operator is used , as such x must be promoted . An int on 32 bits systems , have a typical width of 32 bits , as such it can store all the possible values of an unsigned char , which is limited to only 8 bits . As such the target promotion type is an int . Now that the target type is decided , which is an int , it has a larger width than an unsigned char , as such widening must be performed . Widening does not change signedness , so the unsigned char is widened to an unsigned int , by using zero fill . The resulting value is 1 , and has a bit representation of : 00000000000000000000000000000001 The resulting bits , are interpreted as if , as being of the type signed int , and the negation operator is applied . The result of the negation operator is -1 , which , has a bit representation of : 11111111111111111111111111111111 The value is to be stored in a signed char type , both int and signed char , have the same signedness , as such truncation is applied . The width of a signed char type is 8 bits , as such the leading 24 bits are discarded , and the result is : 11111111 , which is equal to -1 . */

### Function call or return value

When making a function call , and an __ argument is of an integer type , different__ from the target parameter integer type , type conversion occurs .

The argument is __ converted to the parameter type__ , by first truncation , or widening , to the same width , and later on reinterpreting the bits as of the target parameter type .

#include<stdio.h> void trivialFunction( unsigned int val){ if( val == 4294967295){ printf( "%u is equal to 4294967295\n" , val); } else{ printf( "%u is different from 4294967295\n" , val);}} int main( void){ signed char x = -1 ; trivialFunction( x); unsigned char y = x ; trivialFunction( y);} /* Output 4294967295 is equal to 4294967295 255 is different from 4294967295 */ /* In the first call to trivialFunction , the passed argument is of type signed char . trivialFunction expects its argument , to be of the type unsigned int , as such the argument must be converted . First widening to the same width of unsigned int takes place . x is a signed char , as such it is widened to a signed int , by sign extension . The value of x is -1 , and it has a bit representation of 11111111 . The resulting value is -1 , and it has a bit representation of 11111111111111111111111111111111 . This bit pattern , is next reinterpreted as an unsigned int , so 11111111111111111111111111111111 , as an unsigned int , has a value of 4294967295 . This is why trivialFunction prints : 4294967295 is equal to 4294967295 The variable y , has a type of unsigned char . It is assigned the value of x , which is a signed char . Both x , and y have the same width , as such no widening occurs , only the bits of x , are reinterpreted as being unsigned . The bits of x , has a value of -1 , which is 11111111 , when reinterpreted as unsigned , this yield the value of 255 . Next trivialFunction is called with y , as a parameter . Widening occurs , because y is an unsigned char , and the function parameter is an unsigned int . It is done by using zero fill . Hence the value of 1 which has a bit representation of 11111111 , is widened to the value 255 which has a bit representation of : 00000000000000000000000011111111 The function prints : 255 is different from 4294967295 */

When the return value of a function is __ different from its return type__ , the return value is converted to the function’s return type .

### Assignment and initialization

When performing __ assignment or initialization__ to an integer variable , using the

`=`

operator , and the expression to be assigned , is of a different integer type , integer conversion takes place .unsigned char x = 1 ; /* 1 is an integral literal , it is of the type int . The type int has a typical width of 32 bits , while an unsigned char has a typical width of 8 bits . The bits of the int type 00000000000000000000000000000001 are reinterpreted as being the bits of an unsigned int type . The result is : 00000000000000000000000000000001 The unsigned int value is truncated to 8 bits , and the value gotten is 00000001 , which is assigned to the unsigned char x .*/

For further information , __ about the type of integer literals__ , you can check this article .

### Arithmetic operators

When __ one of the following operators__ , is being used , an integer conversion will occur .

* / % + - /* Multiplication , division , modulos , addition , subtraction .*/ < <= > >= == != /* Relational less , less or equal , larger , larger or equal , equal , not equal .*/ & ^ | /* Bitwise and , xor , or .*/ ?: /* The Ternary operator expression , must return a value of a specific type , the second and third operand , are converted to a same type . */ += -= *= /= %= &= ^= |= /* operate and assign operators */

In the case of these operators , __ a common type__ for the operands and the result must be determined . This is done as specified by the following table .

Unsigned | Signed | Rank | Operands and Result type |
---|---|---|---|

uT | sT | uT >= sT | uT |

uT | sT | uT < sT | sT , if sT is capable of holding , all the possible values of : uT . The unsigned type of sT , if sT is not capable of holding all the possible values of : uT . |

uT , means an unsigned type , such as : `unsigned int` sT , means a signed type , such as : `int` The result of relational operators such as < , is always 0 for true , and 1 for false , and it is always of the type int . |

int si = -1 ; unsigned int ui = 0 ; ui = ui + si ; /* int , and unsigned int , have the same rank , one is signed , and the other is unsigned , as such both operands , must be converted to the unsigned int type , and the result of the operation , is of an the unsigned int type . ui is unsigned , so no conversion is necessary . To convert si , to the unsigned int type , and since both si and ui have the same width , the bits of si , which are 11111111111111111111111111111111 , are kept as is , they are only reinterpreted as belonging to a signed type , so now they have a value of 4294967295 . The addition is performed , and the result of the addition operation , is as such : 4294967295 + 0 = 4294967295 , and is of the type unsigned int . ui is of type unsigned int , as such no conversion is necessary , and the result of 4294967295 is stored in ui . */ long long lli = -1 ; ui = lli + ui ; /* lli is of the type long long , it has a higher rank than unsigned int . long long can hold all the values of unsigned int , as such , both operands must be of the type long long . lli is of the type long long , so no conversion is necessary . ui is an unsigned int , it has a bit representation of 11111111111111111111111111111111 it is first extended to unsigned long by using zero fill . 0000000000000000000000000000000011111111111111111111111111111111 After that , the gotten bits are reinterpreted as being of type long long . The gotten value , is the same as the value of ui , which is 4294967295 . The addition is performed between , the two long long integer types , and the result is -1 + 4294967295 , which is equal to 4294967294 , and is of the type long long . ui is of the type unsigned int , the result which is of the type long long must be converted to unsigned int . It is first converted to unsigned long long , the bits pattern does not change , so it remains 0000000000000000000000000000000011111111111111111111111111111110 . Now that is is of the type unsigned long long , it is truncated to the type of ui , which is unsigned int , and it has the format : 11111111111111111111111111111110 , and a value of : 4294967294 . */ long int li = -1 ; li = li + ui ; /* ui is an unsigned int , whereas li is a long int . A long int has a higher rank than an unsigned int . Assuming that on this machine , both long int and unsigned int have a width of 32 bits , this means that long int is not capable of representing all the possible values of the unsigned int type , as such the operands , and the result must be of the unsigned long type . li bits are kept as is , and only reinterpreted as being of the type unsigned long , as such li will have the value of 4294967295 . ui is converted to unsigned long , by widening. unsigned int , and unsigned long have the same width , as such the bits of ui remains the same , 11111111111111111111111111111110 . Adding 4294967295 + 4294967294 = 8589934589 . The result is of the type unsigned long , it cannot fit the width of the unsigned long type , which has a max value of 4294967295 , as such overflow has occurred . The modulo of the result , with regards to 2 to the power of the number of bits , of the unsigned long type is taken . This is equal to 8589934589 % 4294967296 = 4294967293 , which is 11111111111111111111111111111101 The result must be stored in li , li is of the long type , the gotten value is of the type unsigned long , the bits are kept as is , and the result is only reinterpreted as being a signed long , as such the value of li is -3 . */

For __ information on overflow__ of the signed , and unsigned integer types , you can check this , and this article .

### Cast operator

__ __ , when using the casting operator :

`Explicit`

conversion happens `(Type) expression`

. As an example , explicitly casting the `int`

literal `1`

, to the `long`

type .(long) 1

When using the cast operator , this *is called*`explicit`

casting , all other cases of conversion , are called `implicit`

casting .

## Conversion of the floating point types

### Converting from one floating type , to another

Each floating point type , has a different range , and precision . As such , when passing __ from floating point types , with higher__ precision and range , to floating point types with smaller precision and range , a loss of precision , an underflow , or an overflow , can occur .

double dbl = 16777217 ; float fl = dbl ; /* Loss of precision , fl value is 16777216 .*/ dbl = 1.2e-50 ; fl = dbl ; /* Underflow occurs , the behavior is implementation defined , in this case , fl has a value of 0 .*/ dbl = 3.4e100; fl = dbl ; /* Overflow occurs , the behavior is implementation defined , fl has a value of positive infinity */

When passing from a floating point type , with a __ smaller range and precision , to__ a floating point type , with a higher range and precision , no precision loss , underflow , or overflow , occurs . The precision and range are preserved .

float fl = 1.099511627776e12f; double dbl = fl ; /* precision and range are preserved , dbl has a value of : 1.099511627776e12f , which is 2 to the power 40 .*/

### Converting from a floating point type , to an integer type

The process of converting a floating point type , to an integer type , __ can be thought of__ , as if , the number is first converted to the decimal notation , then the fractional part is discarded , then it is represented in its signed or unsigned representation .

double dbl = 1.3f; unsigned char uc = (unsigned char) x ; // uc is equal to 1 dbl = -1.3f; uc = dbl ; // uc is equal to 255

If the floating point number , is __ too large to be represented__ in an integer type , the behavior is not defined by the C standard , it is defined by the implementation .

double dbl = 3.4E30 ; unsigned long ul = dbl ; //ul is equal to 0 unsigned int ui = dbl ; //ui is equal to 0 unsigned char uc = dbl ; //uc is equal to 0 long li = dbl ; //li is equal to -9223372036854775808 int si = dbl ; //si is equal to -2147483648 signed char sc = dbl ; //sc is equal to 0

### Converting from an integer type , to a floating point type

When converting an integer type , to a floating point type , the __ integer value can always be__ represented , but there might be a loss of precision .

int si = 16777216 ; float fl = si ; // fl is equal to 16777216 si = 16777217 ; float fl = si ; // fl is equal to 16777216

### When does the conversion occurs ?

When a floating point type is involved , __ conversion occurs__ , when a function call is made , and the passed argument is of a different type than the function parameter , or when the return value of a function , is of different type , than its return type .

It also happens , when performing floating point variables , assignment and initialization . Finally , it occurs when using arithmetic operators , or when done explicitly , using explicit casting , such as ` (float) 1.0`

.

### Rank of floating point types

The rank of a floating point type , __ is always higher than__ the rank of an integer type , so an integer type , is always converted to a floating point type .

As for the floating point types , they have the following __ ranks__ :

float < double < long double

If an arithmetic operation , __ involves two floating point types__ of different ranks , the floating point type with the lower rank , is converted to the floating point type with the higher rank .

The __ type of the result __ of an operation involving a floating point type , is the same type , as the one determined by the ranking algorithm .

### Floating point types promotion

As with the integer type promotion , when performing arithmetic operations , a floating point type __ can be promoted__ , to a type with a higher precision and range .

This is __ not to be confused with__ how conversion happens , or in which rank , an arithmetic operation involving a floating point type , is to be performed , It is always performed using the higher operand rank .

The __ promotion rule , is defined in__ the macro

`FLT_EVAL_METHOD`

, defined in the header `float.h`

.If `FLT_EVAL_METHOD`

is set to `0`

, then arithmetic operations are done in the type of the widest operand , so if both operands are `float`

, the arithmetic operation is done using the `float`

type , so no promotion occurs .

If `FLT_EVAL_METHOD`

is set to `1`

, then arithmetic operations are performed , by promoting the operands to `long double`

, if any operand is of the `long double`

type , otherwise operands are promoted to the `double`

type , even if both operands are of the `float`

type .

If `FLT_EVAL_METHOD`

is set to `2`

, then arithmetic operations are performed , by promoting the operands , to the `long double`

type .

If `FLT_EVAL_METHOD`

is set to `-1`

, then the behavior is not defined .