No really, when does floating point promotion actually happen? - c++

From this other QUESTION they talk about how Bjarne Stroustrup said that just as integral data-types narrower than an int(e.g. short) are promoted to an int, floats are promoted to a double. However, unlike widening of integrals narrower than an int, floating point promotion does not happen in the same way, but instead, occurs elsewhere.
I know that if you were to compute float + double the float would be converted to a double before the binary operator(+) is applied. However, this is not floating point promotion according to Learncpp.com. This is usual arithmetic conversion.
When does floating point promotion actually happen?

There is such a thing as "floating point promotion" of float to double per [conv.fpprom].
A prvalue of type float can be converted to a prvalue of type double. The value is unchanged.
This conversion is called floating point promotion.
The answers to the linked question are correct. This promotion should not occur automatically when adding two floats since the usual arithmetic conversions do not promote floating-point operands.
Floating point promotion does occur when passing a float as an operand to an ellipsis, like in printf. That's why the %f format specifier prints either a float or a double: if you pass a float, the function actually receives a double, the result of promotion.
The existence of the floating point promotion is also important in overload resolution, because integral promotions and floating point promotions have better implicit conversion rank than integral conversions, floating point conversions, and floating-integral conversions.
Example 1:
void f(double);
void f(long double);
f(0.0f);
This calls void f(double) since the promotion to double is better than the conversion to long double. In contrast, consider this perhaps surprising example 2:
void f(long double);
void f(int);
f(0.0f);
This is ambiguous. The conversion from float to long double is no better than the conversion from float to int since they are both not promotions.
Example 3:
struct S {
operator float();
operator int();
};
double d = S();
This calls operator float and then promotes the resulting float value to double to initialize d.

The primary (perhaps sole) time that floating point promotions are applied is when passing an argument to a variadic function (e.g., printf).
In this case, the usual arithmetic conversions don't apply (they're for finding a common type between two operands in an expression).
The relevant part of the standard is [expr.call]/7 (at least as of N4296):
When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.10).
[...]
If the argument has integral or enumeration type that is subject to the integral promotions (4.5), or a floating point type that is subject to the floating point promotion (4.6), the value of the argument is converted to the promoted type before the call.

Related

Why Implicit Cast from double to float available?

In C++ we can write something like
float f = 3.55;
and it is a legal statement, whereas the type of real number numerals is double and we are storing that double into floating point number. It essentially means storing 8 bytes into 4 bytes (a possible data loss)? My question is that when I write
long l = 333;
int y = l;
I get an error because long value is converted into int value (possible data loss). but why don't I encounter a problem when storing 8 byte double real numeral in floating point (4 byte)?
From §4 Standard conversions [conv] C++11:
Standard conversions are implicit conversions with built-in meaning.
Clause 4 enumerates the full set of such conversions. A standard
conversion sequence is a sequence of standard conversions in the
following order:
...
Zero or one conversion from the following set: integral promotions,
floating point promotion, integral conversions, floating point
conversions, floating-integral conversions, pointer conversions,
pointer to member conversions, and boolean conversions.
So conversion between two numeric types is allowed implicitly as it makes sense also if used carefully. For example When you calculate Amount(int) from P(int), R(float) and T(int);
And from §4.8 Floating point conversions [conv.double],
A prvalue of floating point type can be converted to a prvalue of
another floating point type. If the source value can be exactly
represented in the destination type, the result of the conversion is
that exact representation. If the source value is between two adjacent
destination values, the result of the conversion is an
implementation-defined choice of either of those values. Otherwise,
the behavior is undefined.
The conversions allowed as floating point
promotions are excluded from the set of floating point conversions.
It appears double to float conversion is implicitly performed by the compliant C++ compiler. (At the cost of potentially loosing the precision)
Your example is not an error and should compile.
When you assign a larger integer type to a smaller integer type (or perform any conversion that doesn't quality as a promotion), an integral conversion occurs and precision may be lost.
Similarly, floating point conversion occurs when you assign one floating point type to another floating point type; the result is either the same value, or a value close to it, unless the source value exceeds the range of the destination type.

Is conversion and promotion the same thing?

I am not sure if promotion just means converting a data type to a larger data type (for example short to int).
Or does promotion means converting a data type to another "compatible" data type, for example converting a short to an int, which will keep the same bit pattern (the extra space will be filled with zeros). And is conversion means converting something like an int to a float, which will create a completely different bit pattern?
There are two things that are called promotions: integral promotions and floating point promotions. Integral promotion refers to integral types (including bitfields and enums) being converted to "larger" integral types and floating point promotion is specifically just float to double.
Both types of promotions are subsets of a wider range of conversions.
char -> int: integral promotion
float -> double: floating point promotion
int -> char: [narrowing] conversion (not a promotion)
int -> float: conversion
const char* -> std::string: conversion
Foo -> Bar: possibly undefined conversion?
etc.
A promotion is a specific kind of conversion for built-in types that is guaranteed not to change the value.
The type you are promoting to must be able to accurately represent any possible value of the type you are promoting from.
Here is a list of the applicable conversions.
Promotion
char or short values (signed or unsigned) are promoted to int (or unsigned) before anything else happens
this is done because int is assumed to be the most efficient integral datatype, and it is guaranteed that no information will be lost by going from a smaller datatype to a larger one
Conversion
after integral promotion, the arguments to an operator are checked
if both are the same datatype, evaluation proceeds
if the arguments are of different datatypes, conversion will occur
Casts
the type of an expression can be forced using casts.
a cast is simply any valid datatype enclosed in parentheses and placed next to a constant, variable or expression
Please Refer to this : website

C++ How to correctly round a const float to unsigned int

So I have a const float that has a range from 0.0 to 1.0. What is the correct way to cast this to an unsigned int either by rounding or truncating?
Would this be acceptable?
const float f=0.5f;
unsigned int i=static_cast<unsigned int>(f);
Yes, that's perfectly acceptable if you want to truncate, and if you don't care what happens with negative numbers or overflow.
If you want to round, call roundf first (or, if you want a different rounding rule than "half rounds away from zero", write your own code).
If you want to deal with negative numbers or overflow, you need to check before converting.
According to 5.2.9 in the standard, static_cast in this case is defined to give you the same value as unsigned int i(f). And I think most style guides would agree that the static_cast is preferred (as making casts explicit and noticeable is usually a good thing).
In more detail:
According to 4.9.1:
A prvalue of a floating point type can be converted to a prvalue of an integer type. The conversion trun- cates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.
I'm not sure exactly how the const-away works in C++14, but I believe it's not a qualification conversion, but part of the lvalue-to-rvalue conversion from 4.1.1:
A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue … If T is a non-class type, the type of the prvalue is the cv-unqualified version of T.
So, f has one lvalue-to-rvalue conversion from lvalue const float to rvalue float, then one floating-integral conversion from float to unsigned int, so by 4.0.1 it's a standard conversion, so by 5.2.9 it's valid as a static_cast.
Just add one half to round:
unsigned int i = static_cast<unsigned int>(f + 0.5);
Or nothing to truncate - what you have is fine (assuming f >= 0)
unsigned int i = static_cast<unsigned int>(f);

How does the compiler choose which value to give auto?

First off, I am aware that this is an extremely simple question. I'm just looking for a technical explanation as to why the compiler decides to make the following variable with an auto type specifier the type double over int:
int value1 = 5;
double value2 = 2.2;
auto value3 = value1 * value2;
I know that the compiler will derive the double type for value3 from the initialized value, but why exactly is that?
auto variable types are defined in terms of template type deduction. Like this:
template<typename T>
void f(T t);
f(value1 * value2); // will call f<double>()
The reason value1 * value2 gives double rather than int is because the arithmetic conversion rules allow turning int into double (the reverse is an implicit conversion also but not an arithmetic conversion). When you use operators on built-in types, "the usual arithmetic conversions are applied".
Here's the rule found in section 5 (Expressions) of the Standard:
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result.This pattern is called the usual arithmetic conversions, which are defined as follows:
If either operand is of scoped enumeration type, no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.
If either operand is of type long double, the other shall be converted to long double.
Otherwise, if either operand is double, the other shall be converted to double.
Otherwise, if either operand is float, the other shall be converted to float.
Otherwise, the integral promotions shall be performed on both operands.
Because when multiplying an int by a double you get a double.
C and C++ compilers always promote basic numeric types to the most general type included in an expression. So, any expression involving two int values yields an int, but if either of the operands is double, then the expression value will also be double.

How a floating point literal is treated either double or float in Visual C++?

Suppose float a = (1.5 * b) where b is float then how is this expression evaluated?
Is 1.5 treated as double or float?
1.5 is double, use 1.5f for float, what it actually does:
float a = (float)(1.5 * (double)b)
1.5 is a floating point literal, a double value. C++03 2.13.3 Floating literals has this to say:
A floating literal consists of an integer part, a decimal point, a fraction part, an e or E, an optionally signed integer exponent, and an optional type suffix. ... The type of a floating literal is double unless explicitly specified by a suffix.
Section 13.3.3.1 Standard conversion sequences defines the way in which conversions are handled but it's a little dry to repeat here. Suffice to say that floating point promotion is done and section 4.6 Floating point promotion states that:
An rvalue of type float can be converted to an rvalue of type double. The value is unchanged.
Hence the float b is promoted to a double to perform the multiplication.
Then the calculation is performed using (effectively) a temporary double and the result is shoe-horned back into the float a.
So, effectively:
float b = something;
double xyzzy0 = 1.5;
double xyzzy1 = (double)b;
double xyzzy2 = xyzzy0 * xyzzy1;
float a = xyzzy2;
That last step may be problematic. Section 4.8 Floating point conversions (which doesn't include the safer promotions like float to double) states:
An rvalue of floating point type can be converted to an rvalue of another floating point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.
The conversions allowed as floating point promotions are excluded from the set of floating point conversions.
In other words, if the multiplication results in a value outside the range of a float, all bets are off. This is likely to happen if b is about at 67% of the maximum absolute value of a float (positive or negative, doesn't matter).