When you have
c = a / b :
- If both a and b are integer types, c is the result truncated (rounded towards zero) to the nearest integer, even if c is a float or a double.
- If a or b is a float, and the other is a float or an integer type, c is the result of the division using float precision and range, converted to whatever type c has if it is not a float.
- If a or b is a double, c is the result of the division using double precision and range, converted to whatever type c has if it is not a double.
Typically,
float has about seven, and
double about 15 significant digits.
The reason for this behaviour is C++ implicit type promotion rules; the same applies to all standard arithmetic operators (+, -, *, /). In a promotion, the value stays unchanged [note below], but the type changes.
If an operand is an
int or an integer type smaller than
int, it is promoted to
int.
If one operand is a
long and the other operand is (or was promoted to)
int , the other operand is promoted to
long.
If one operand is a
long long and the other operand is (or was promoted to)
int or
long, the other operand is promoted to
long long.
If one operand is a
long double, the other operand is promoted to
long double too.
If one operand is a
double, and the other operand is an integer type or a
float, the other operand is promoted to
double.
If one operand is a
float, and the other operand is an integer type, the other operand is promoted to
float.
The operation is then done at the range and precision of the common type; the result (in an expression) has that same type.
If the result is assigned to a variable of an integer type, the result is truncated (rounded towards zero);
if the result is assigned to a floating-point variable with less precision than the result, the extra precision is discarded (using the current floating-point rounding method wrt. the discarded bits and the least significant unit in the result).
It is easier to remember that for integer types, the base type is
int, with the next higher type being
long (AKA
long int), and the highest type being
long long (AKA
long long int).
unsigned types promote to unsigned types, but if one is unsigned and the other signed, both are promoted to signed types.
For floating point types, the types are
float,
double,and
long double.
If one is an integer type (signed or unsigned), and the other is a floating-point type, the integer type operand is promoted to the same floating-point type.
[note]: When an integer type is promoted to a floating-point type, it is possible for the floating-point value to differ, if the magnitude of the value is large enough. For example, promoting an
int 2147483584 to a
float (on typical systems where float is IEEE-754 Binary32), yields value
2147483584.0f, which is off by 64. On typical systems, the exactly represented integer ranges are
-16777216..16777216 for
floats, and
-9007199254740992..9007199254740992 for
doubles; the range for
long double varies but is at least as large as for
doubles. Values larger in magnitude will be "rounded" to the nearest representable value in the target floating-point type.
To "promote" any operand to another type, simply
cast it. Explicit casts precede implicit casts, so you can easily control the expression using a single cast.
For example,
c = (double)a / b causes
a, and implicitly also
b to be promoted to (at least)
double prior the division.
c = a / (double)b has the same effect; it does not matter which operand you cast.
Casting the result
c makes no sense, though.
A cast has the side effect of promoting or reducing the value to the target type precision and range. So, for example,
c = (float)(a / (double)b)
calculates the division using at least
double precision and range, but then limits the result to
float precision and range, before (converting to whatever type
c has) and assigning to
c.
Usually, you don't need this, but if you ever find yourself implementing e.g. Kahan sum algorithm or other similar numerically sensitive algorithms, this may come in very useful. (In particular, you do not need to try and disable optimizations to get such numerically sensitive code working correctly, if you use such explicit casts.)