This question already has answers here:
Implicit type conversion rules in C++ operators
(9 answers)
Closed 2 years ago.
I been programming on C and C++ for some time, and was always sure that double dividing int gives double, or double dividing int also give double (only int by int gives int) and same with adding. I was doing my assignment and was sure that 1 + (f*f + 10) / k, where f is double and k is int would always return double.
I was using g++ -std=gnu++11 command on mac (so it's clang compiler probably) and I got tests passed (I indeed got float number as a result), but my teacher says that it not for sure that it will be float (he is using windows). Is that behavior platform specific? Is there any C++ standard that describes double on int division? Thank you!
My code:
#include <iostream>
using namespace std;
int main() {
int N;
double f, P = 0;
cin >> N >> f;
for (double k = 1; k <= N; k++){
P += 1 + (f*f + 10) / k;
}
cout << P;
return 0;
}
From the C++20 standard draft [expr.arith.conv]:
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:
(1.1)
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.
(1.2)
If either operand is of type long double, the other shall be converted to long double.
(1.3)
Otherwise, if either operand is double, the other shall be converted to double.
(1.4)
Otherwise, if either operand is float, the other shall be converted to float.
(1.5)
Otherwise, the integral promotions ([conv.prom]) shall be performed on both operands.56 Then the following rules shall be applied to the promoted operands:
...
This paragraph has not changed in any fundmental way since at least C++11 (which was the oldest I checked), so your teacher has some explaining to do.
As you can read here:
[...] if either operand is double, the other operand is converted to double
[...] if either operand is float, the other operand is converted to float
so it's guaranteed by the standard that that operation will gives you back a float
If an expression contains two arguments, and one is a double, and the other an int, then the type of that expression is always a double.
If an expression contains two arguments, and one is a float, and the other an int, then the type of that expression is always a float.
In many ways, the second statement is becoming anachronistic (as 64 bit int is perhaps just round the corner). Note that in your snippet you are using a double rather than a float.
Some template metaprogramming code (!) for you to show your teacher:
#include <iostream>
#include <type_traits>
int main(){
// Will output 1 if the types are the same, 0 otherwise
std::cout << std::is_same<decltype(int{} + double{}), double>::value;
}
Where you can replace double and int with types of your own choosing at your leisure: what the code does is compare the type of int{} + double{} with double.
Related
For the below expression:-
int main()
{
unsigned ui = 1;
float fval = 2.5;
cout << sizeof(ui) << endl << sizeof(fval) << endl;
cout << typeid(ui+fval).name();
}
we get the following output:-
4
4
f
It seems that ui+fval is a float.
However, given that both float and unsigned int are 4 bytes, and that not all of the unsigned int values can fit inside a float, shouldn't fval be converted to unsigned int?
The rules for arithmetic operators are actually slightly different than the rules for general function overload resolution.
From cppreference on arithmetic operators:
Conversions
If the operand passed to an arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion. If an operand has array or function type, array-to-pointer and function-to-pointer conversions are applied.
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
[snip]
So, quite explicitly, when we're doing unsigned + float, the unsigned gets converted to a float. It's the first rule that applies, so we follow it.
However, for general overload resolution, the conversion from unsigned to float is equivalent to the conversion from float to unsigned. So for instance in the following code snippet:
unsigned add(unsigned l, unsigned r) { return l + r; }
float add(float l, float r) { return l + r; }
int main()
{
unsigned ui = 1;
float fval = 2.5;
add(ui, fval);
}
It's unable to decide which version of add to use and fails to compile.
I'm trying to understand the implicit conversion rules in C++ and I understood that when there are one operation between two primary types the "lower type" is promoted to the "higher type", so let say for:
int a = 5;
float b = 0.5;
std::cout << a + b << "\n";
should print 5.5 because 'a' gets promoted to float type. I also understood that unsigned types are "higher types" than the signed counter parts so:
int c = 5;
unsigned int d = 10;
std::cout << c - d << "\n";
prints 4294967291 because 'c' gets promoted to a unsigned int and since unsigned types wraps around when less than zero we get that big number.
However for the following case I don't understand why I am getting -105 instead of a positive number.
#include <iostream>
int main(void) {
unsigned char a = 150;
std::cout << static_cast<int>(a - static_cast<unsigned char>(255)) << "\n";
return 0;
}
I guess that this code:
a - static_cast<unsigned char>(255)
should result in a positive number so the final cast (to int) shouldn't affect the final result right?
You're missing the (implicit) conversion from unsigned char to int that happens to perform the - (subtract) operation. This integer promotion happens any time you try to apply any integer operation to a value of some integral type smaller than int.
Quoting from C++14, chapter § 5.7
The additive operators + and - group left-to-right. The usual arithmetic conversions are performed for
operands of arithmetic or enumeration type.
and for usual arithmetic conversions, (specific for this case)
....
Otherwise, the integral promotions (4.5) shall be performed on both operands
and, finally, for integral promotions, chapter § 4.5
A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion
rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all
the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned
int.
Hence, the unsigned char operands are promoted to int and then , the result is calculated.
There are answers here showing what is happening. I won't repeat. I am going to give you a simple tool to help you.
Here is a trick you can do to quickly find the type of an expression:
template <class> struct Name; // purposely no definition given
Name<decltype(your_expression)> n;
This will generate a compiler error for undefined template 'Name', but what we are really interested in is the type of the template argument which will appear in the error message.
E.g. if you want to see what type you get when you do arithmetic between two unsigned char:
#include <utility>
template <class> struct Name;
auto test()
{
Name<decltype(std::declval<unsigned char>() - std::declval<unsigned char>())> n;
// or
unsigned char a{};
Name<decltype(a - a)> n2;
}
will get you
error: implicit instantiation of undefined template 'Name<int>'
which will show you that the type of the expression is int
Of course this won't tell you the rules involved, but it is a quick starting point to see the type of the expression or to verify your assumption of the type of the expression.
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.
I have the following piece of code (feel free to change the float with double):
class A
{
public:
void setValueFloat(float v) {
m_floatValue = v / 3.6; // m/s <-> km/h conversion
}
void setValueInt(int v1, int v2) {
m_intValue1 = v1; m_intValue2 = v2;
}
bool conditionIsOk()
{
if(m_intValue1 > m_intValue2)
{
if(m_intValue1 - m_intValue2 > m_floatValue)
{
return true;
}
}
return false;
}
private:
int m_intValue1, m_intValue2;
float m_floatValue;
};
and somewhere else:
A a;
int something = 5; // Intentionally int
int somethingElse = 6; //these are just some numbers, not production data!!!
int moreStuff = 7;
a.setValueFloat(something);
a.setValueInt(somethingElse, moreStuff);
if(a.conditionIsOk())
{
// Yippee!
}
And the questions:
How safe is it to compare the result of an arithmetic operation on ints to a float given the situation above?
Is it necessary to (float)m_intValue1 - (float)m_intValue2 > m_floatValue for this situation?
Where in the C / C++ standard can I find a line about exactly this situation?
What typecasts will be done by default for the simple situation m_intValue1 - m_intValue2 > m_floatValue ? (and how can I show this to someone else in a way that he also sees it (visually), "just believing that it works" is not enough :) )
This depends on the actual implementation (i.e. which compiler and which architecture are used). On typical systems with 32 bit ints and IEEE754 binary32 floats integers can be represented exactly up to +-2^24 as floats, so not for the full range of possible values. So no, it is not safe in general, but may be safe if the used range of your integers (or in this case rather the difference!) and floats is limited appropriately.
No! In fact m_intValue1 - m_intValue2 > m_floatValue is better as the conversion to float happens after the computation of the difference (see note about difference in the above point). You can be explicit and write static_cast<float>(m_intValue1 - m_intValue2) > m_floatValue, but this is not necessary.
Conversions are covered in chapter 4 of the C++ standard (see draft N3242). In particular 4.9 Floating-integral conversions, also note 5§10 "usual arithmetic conversions" which also applies to comparisons. As the question is also tagged with C, in the C standard (see darft N1570) the corresponding section is 6.3.1 and in particular 6.3.1.4 and 6.3.1.8.
See answers to 2. and 3.
The usual rules on type promotion in binary operations apply. To quote the Standard (chapter 5. expressions)
9.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 (7.2), 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 (4.5) shall be performed on both operands
I want to make sure that my understanding of the return type of C++ division,
int / int => return is int?
float / float => return is which type? float?
double /double => return is double?
int / double => return is double?
int / float => return is float?
Please correct me if I am wrong.
All of those are correct. Here's what the C++03 standard says (§5/9):
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 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 (4.5) shall be performed on both operands.
Then, if either operand is unsigned long the other shall be converted to unsigned long.
Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent all the values of an unsigned int, the unsigned int shall be converted to a long int; otherwise both operands shall be converted to unsigned long int.
Otherwise, if either operand is long, the other shall be converted to long.
Otherwise, if either operand is unsigned, the other shall be converted to unsigned.
[Note: otherwise, the only remaining case is that both operands are int]
operator/ for basic data types (just like most, if not all, operators for basic types) returns the strongest type of its two operands.
The answer to all of your questions is thus yes.
In general, floating point types are stronger than integer ones and unsigned are stronger than signed...
Defining > as "stronger than", we can say that:
long double > double > float > unsigned long > long > unsigned int > int > unsigned short > short > unsigned char > char
You are correct in all cases. The rules for operations involving at least one floating point type are that if either type is a long double, the result is long double; otherwise, if either type is double the result is double otherwise the result has type float.
Arithmetic operations between two ints produce an int result.
The rules between other types are slightly more complex and can be implementation dependent; for almost all operations integer promotions mean that the operands are promoted to at least an int sized types producing at least an int sized result.
Considering only three types (float, double and int):
If any of the operand is double, then the result will be double.
Else if any of the operand is float, then the result will be float.
Else the result will be int .
The result will be typed (if rule exists) for the assignment. If you have int x = dY + iZ; Then promotion will cause the addition result to be double, but it will be converted to an int when its assigned to x. Google "variable promotion in c++" for more details.
Roughly speaking, in C++, in any scenario, both operands are converted to the "largest" type of the two operands' types before the operation is executed. See MSDN Standard Arithmetic Conversions.