About comparing an integer and a float/double in C/C++ - c++

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

Related

Double divided by double and by integer: which one is better?

I always assume that dividing a double by an integer will lead to faster code because the compiler will select the better microcode to compute:
double a;
double b = a/3.0;
double c = a/3; // will compute faster than b
For a single operation it does not matter, but for repetitive operations it can make difference. Is my assumption always correct or compiler or CPU dependent or whatever?
The same question applies for multiplication; i.e. will 3 * a be faster than 3.0 * a?
Your assumption is not correct because both your divide operations will be performed with two double operands. In the second case, c = a/3, the integer literal will be converted to a double value by the compiler before any code is generated.
From this Draft C++ Standard:
8.3 Usual arithmetic conversions          [expr.arith.conv]
1    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.3) –
Otherwise, if either operand is double, the other shall be converted
to double.
Note that, in this Draft C11 Standard, §6.3.1.8 (Usual arithmetic conversions) has equivalent (indeed, near-identical) text.
There is no difference. The integer operand is implicitly converted to a double, so they end up practically equivalent.

Does dividing float by int always give float? C++ [duplicate]

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.

Is there an implicit type promotion in "float = float - float"?

We are using QA-C for MISRA C++ conformance, but the tool spews out an error for code like this:
float a = foo();
float b = bar();
float c = a - b;
As far as I understand, this has no implicit type promotion as everything will happen in float-sized chunks, but the tool tells me that the subtraction causes one. Is there any situation where there might be implicit promotion?
There is no implicit promotion involved here.
When conversions involving binary operators are involved, they are called usual arithmetic conversions.
From C++ standard, [expr]/11:
11 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:
...
(11.4) — Otherwise, if either operand is float, the other shall be converted to float.
Since both operands are float in your example, there is no such conversion or promotion.
So this could be a false positive from the tool.

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.

Why does C/C++ automatically convert char/wchar_t/short/bool/enum types to int?

So, if I understood it well, integral promotion provides that: char, wchar_t, bool, enum, short types ALWAYS are converted to int (or unsigned int). Then, if there are different types in an expression, further conversion will be applied.
Am I understanding this well?
And if yes, then my question: Why is it good? Why? Don't become char/wchar_t/bool/enum/short unnecessary? I mean for example:
char c1;
char c2;
c1 = c2;
As I described before, char ALWAYS is converted to int, so in this case after automatic converting this looks like this:
int c1;
int c2;
c1 = c2;
But I can't understand why is this good, if I know that char type will be enough for my needs.
Storage types are never automatically converted. You only get automatic integer promotion as soon as you start doing integer arithmetics (+, -, bitshifts, ...) on those variables.
char c1, c2; // stores them as char
char c3 = c1 + c2; // equivalent to
char c3 = (char)((int)c1 + (int)c2);
The conversions you're asking about are the usual arithmetic conversions and the integer promotions, defined in section 6.3.1.8 of the latest ISO C standard. They're applied to the operands of most binary operators ("binary" meaning that they take two operands, such as +, *, etc.). (The rules are similar for C++. In this answer, I'll just refer to the C standard.)
Briefly the usual arithmetic conversions are:
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.
Otherwise, the integer promotions are performed on both operands, and then some other rules are applied to bring the two operands to a common type.
The integer promotions are defined in section 6.3.1.1 of the C standard. For a type narrower than int, if the type int can hold all the values of the type, then an expression of that type is converted to int; otherwise it's converted to unsigned int. (Note that this means that an expression of type unsigned short may be converted either to int or to unsigned int, depending on the relative ranges of the types.)
The integer promotions are also applied to function arguments when the declaration doesn't specify the type of the parameter. For example:
short s = 2;
printf("%d\n", s);
promotes the short value to int. This promotion does not occur for non-variadic functions.
The quick answer for why this is done is that the standard says so.
The underlying reason for all this complexity is to allow for the restricted set of arithmetic operations available on most CPUs. With this set of rules, all arithmetic operators (other than the shift operators, which are a special case) are only required to work on operands of the same type. There is no short + long addition operator; instead, the short operand is implicitly converted to long. And there are no arithmetic operators for types narrower than int; if you add two short values, both arguments are promoted to int, yielding an int result (which might then be converted back to short).
Some CPUs can perform arithmetic on narrow operands, but not all can do so. Without this uniform set of rules, either compilers would have to emulate narrow arithmetic on CPUs that don't support it directly, or the behavior of arithmetic expressions would vary depending on what operations the target CPU supports. The current rules are a good compromise between consistency across platforms and making good use of CPU operations.
if I understood it well, integral promotion provides that: char, wchar_t, bool, enum, short types ALWAYS converted to int (or unsigned int).
Your understanding is only partially correct: short types are indeed promoted to int, but only when you use them in expressions. The conversion is done immediately before the use. It is also "undone" when the result is stored back.
The way the values are stored remains consistent with the properties of the type, letting you control the way you use your memory for the variables that you store. For example,
struct Test {
char c1;
char c2;
};
will be four times as small as
struct Test {
int c1;
int c2;
};
on systems with 32-bit ints.
The conversion is not performed when you store the value in the variable. The conversion is done if you cast the value or if you perform some operation like some arithmetic operation on it explicitly
It really depends on your underlying microprocessor architecture. For example, if your processor is 32-bit, that is its native integer size. Using its native integer size in integer computations is better optimized.
Type conversion takes place when arithmetic operations, shift operations, unary operations are performed. See what standard says about it:
C11; 6.3.1.4 Real floating and integer:
If an int can represent all values of the original type (as restricted by the width, for a
bit-field), the value is converted to an int; otherwise, it is converted to an unsigned
int. These are called the integer promotions.58) All other types are unchanged by the
integer promotions.
58.The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators,1 as specified by their respective subclauses
1. Emphasis is mine.