I have this simple code that converts between degrees Farehnheit and degrees Celcius. I defined some macros to do it, but I'm getting some weird results when I use it. I use this method when absoluteTemp = 373.15 (the boiling point of water in kelvin).
#define kelvinToCelc(k) k - 273.15
#define celcToFahren(c) (9.0 / 5.0) * c + 32
double x = kelvinToCelc(absoluteTemp); // 100
double y = celcToFahren(x); // 212
double z = celcToFahren(kelvinToCelc(absoluteTemp)); // 430.52???
return celcToFaren(kelvinToCelc(absoluteTemp));
After expanding macro in
double z = celcToFahren(kelvinToCelc(absoluteTemp));
It becomes
double z = (9.0 / 5.0) * absoluteTemp - 273.15 + 32
You need to add parentheses to the macros
#define kelvinToCelc(k) (k - 273.15)
#define celcToFahren(c) ((9.0 / 5.0) * c + 32)
The old rule was : Use more parenthesis in macros around everything:
#define kelvinToCelc(k) ((k) - 273.15)
#define celcToFahren(c) ((9.0 / 5.0) * (c) + 32)
Notice parens around the whole macro and all macro arguments
The new rule is : Use inline functions They have typechecking, evaluate arguments only once, and because they don't need so many parenthesis *
Note: * Some exceptions may apply, this is not one of them
This is what that looks like as inline functions
inline double kelvinToCelc(double k)
{
return k - 273.15;
}
inline double kelvinToCelc(double c)
{
return (9.0 / 5.0) * c + 32;
}
Notice that you have to put inline and the return type before the name, add types to all arguments, and add a ; at the end
Notice how you can use newlines to make it easier to read, and also so you can step into it in the debugger
Macros are simple - Just a text replacement
i.e.
double z = celcToFahren(kelvinToCelc(absoluteTemp));
becomes
double z = (9.0 / 5.0) * kelvinToCelc(absoluteTemp) + 32
Then becomes
double z = (9.0 / 5.0) * absoluteTemp - 273.15 + 32
Now just do the maths
i.e.
double z = (9.0 / 5.0) * 373.15 - 273.15 + 32;
If you are going to use macros, make your life easier with parenthesis
#define kelvinToCelc(k) (k) - 273.15
#define celcToFahren(c) (9.0 / 5.0) * (c) + 32
This helps prevent the unexpected results you are seeing. The reasons have already been pointed out in other posts
Just one thing i should add to the other answers, try running just the preprocessor and look at the output ie:
g++ -E -P main.cpp
The answer 431.52 is correct. It expanded like this
(9.0/5.0) * 373.15 - 272.15 + 32
In mathematics [* and /] take precedence over [+ and -]. So the equation expanded like
((9.0/5.0) * 373.15) - 272.15 + 32
(671.67) - 272.15 + 32
399.52 + 32
431.52
[* and /] have same precedence so there order doesn't matter and similarly [+ and -] have same precedence so their execution order doesn't matter.
For macros, it just relies on textual substitution. So it is equivalent to:
double z = (9.0 / 5.0) * absoluteTemp - 273.15 + 32;
That's why you got wrong result.
Edit:
Try to use (inline) functions instead even you can make it work by adding more parenthesis: (see #2)
#define celcToFahren(c) ((9.0 / 5.0) * (c) + 32)
Macros are also error-prone because they rely on textual substitution and do not perform type-checking. Check out here for more info.
Related
I would like to know how can I convert this equation to C++.
This is what I've tried.
void calPoints(int arti, int eksi, int hak) {
int bir = 1, bin = 1000, on = 10, bes = 5;
int puan = on * arti + bes * eksi + bir / hak * bin - arti * eksi / arti + eksi;
}
There are two things to be wary of when writing C++ code for this equation: Operator precedence and integer math.
Operator precedence is easy enough to handle, and is only a concern for the last term. The more interesting issues come from integer division.
The expression 1 / hak will either be 0, 1, -1, or a divide-by-zero error, because the result of integer division is an integer. 1 / 2 will be 0. The usual fix is to multiply first, then divide (as long as this ordering won't result in overflow). In this case, we multiply by 1000 first, then divide.
Putting that all together gives:
int puan = on * arti + bes * eksi + bir * bin / hak - arti * eksi / (arti + eksi);
Although bir can be left out since it is 1, and my personal inclination would be to leave the constants as constants in the equation, which makes it easier to verify:
int puan = 10 * arti + 5 * eksi + 1000 / hak - arti * eksi / (arti + eksi);
If integer based math is not desired, you'll need to change a few types to do things with floats or doubles.
double puan = 10.0 * arti + 5.0 * eksi + 1000.0 / hak - double(arti * eksi) / (arti + eksi);
In this form, all the constants are specified as double values, so that each term is evaluated as a double, with one cast in the last term to also calculate that as a double. This result can be cast to an int if necessary. The parameter types may need to be changed to double, depending on what values may need to be passed into the equation.
- arti * eksi / arti + eksi;
That's where your problem is. Think about the order of operations, it does multiplication and division before addition and subtraction.
Steps the code is taking:
arti * eksi
the result above / arti
the result above + eski
You need a parenthesis around the arti + eksi
double puan = (on * arti) + (bes * eski) + ((double)(bir/hak))*bin - (double)((arti*eski)/(arti+esi))
Puan is double because (bir/hak) and ((arti*eski)/(arti+esi)) might result in floating point number.
If you don't want it to be double then,
int puan = (on * arti) + (bes * eski) + (bir/hak)*bin - ((arti*eski)/(arti+esi))
This should work in your case. You should read about typecasting and precedence of arithmetic operators.
Consider the following piece of code showing some simple arithmetic operations
int result = 0;
result = c * (a + b) + d * (a + b) + e;
To get the result in the expression above the cpu would need to execute two integer multiplications and three integer additions. However algebraically the above expression could be simplified to the code below.
result = (c + d) * (a + b) + e
The two expressions are algebraically identical however the second expression only contains one multiplication and three additions. Is gcc (or other compilers for that matter) able to make this simple optimization on their own.
Now assuming that the compiler is intelligent enough to make this simple optimization, would it be able to optimize something more complex such as the Trapezoidal rule (used for numerical integration). Example below approximates the area under sin(x) where 0 <= x <= pi with a step size of pi/4 (small for the sake of simplicity). Please assume all literals are runtime variables.
#include <math.h>
// Please assume all literals are runtime variables. Have done it this way to
// simplify the code.
double integral = 0.5 * ((sin(0) + sin(M_PI/4) * (M_PI/4 - 0) + (sin(M_PI/4) +
sin(M_PI/2)) * (M_PI/2 - M_PI/4) + (sin(M_PI/2) + sin(3 * M_PI/4)) *
(3 * M_PI/4 - M_PI/2) + (sin(3 * M_PI/4) + sin(M_PI)) * (M_PI - 3 * M_PI/4));
Now the above function could be written like this once simplified using the trapezoidal rule. This drastically reduces the number of multiplications/divisions needed to get the same answer.
integral = 0.5 * (1 / no_steps /* 4 in th case above*/) *
(M_PI - 0 /* Upper and lower limit*/) * (sin(0) + 2 * (sin(M_PI/4) +
sin(3 * M_PI/4)) + sin(M_PI));
GCC (and most C++ compilers, for that matter) does not refactor algebraic expressions.
This is mainly because as far as GCC and general software arithmetic is concerned, the lines
double x = 0.5 * (4.6 + 6.7);
double y = 0.5 * 4.6 + 0.5 * 6.7;
assert(x == y); //Will probably fail!
Are not guaranteed to be evaluate to the exact same number. GCC can't optimize these structures without that kind of guarantee.
Furthermore, order of operations can matter a lot. For example:
int x = y;
int z = (y / 16) * 16;
assert(x == z); //Will only be true if y is a whole multiple of 16
Algebraically, those two lines should be equivalent, right? But if y is an int, what it will actually do is make x equal to "y rounded to the lower whole multiple of 16". Sometimes, that's intended behavior (Like if you're byte aligning). Other times, it's a bug. The important thing is, both are valid computer code and both can be useful depending on the circumstances, and if GCC optimized around those structures, it would prevent programmers from having agency over their code.
Yes, optimizers, gcc's included, do optimizations of this type. Not necessarily the expression that you quoted exactly, or other arbitrarily complex expressions. But a simpler expresion, (a + a) - a is likely to be optimized to a for example. Another example of possible optimization is a*a*a*a to temp = a*a; (temp)*(temp)
Whether a given compiler optimizes the expressions that you quote, can be observed by reading the output assembly code.
No, this type of optimization is not used with floating points by default (unless maybe if the optimizer can prove that no accuracy is lost). See Are floating point operations in C associative? You can let for example gcc do this with -fassociative-math option. At your own peril.
I have a C++ code and an Octave that both compute the same equation
In C++
#include <math.h>
int main()
{
float x = 1.5f;
float y = pow(x, 6) * 235809835.41f - pow(x, 5) * 2110439254.2f + pow(x, 4) *7869448124.8f - pow(x, 3) * 15648965509.0f + pow(x, 2) * 17503313074.0f - (x)* 10440563329.0f + 2594694745.0f; // result y = 3584
return 0;
}
In Octave
x = 1.5
y = (x ^ 6) * 235809835.41 - (x ^ 5) * 2110439254.2 + (x ^ 4) *7869448124.8 - (x ^ 3) * 15648965509 + (x ^ 2) * 17503313074 - (x)* 10440563329 + 2594694745 // result y = 26
The computed value of y differs in the two cases. C++ computes y to be 3584 and Octave computes y to be 26. What could be the cause for this divergence?
EDIT : Excel produces the same result as Octave, and the result is logical too within the context of the equation. So, something is wrong with the C++ code or compiler.
This appears to be due to the limited precision of the float type, which is likely causing one of the operations to be effectively discarded because one operand is of a smaller enough magnitude than the other to cause a significant change to the result. (See this extremely contrived example that shows what this might look like.)
If you rewrite the code to use the double type, which is more precise, then the result is 26.810783, which matches the result I get from evaluating the formula in Maxima.
Further reading: What Every Computer Scientist Should Know About Floating-Point Arithmetic
I'm new to functions and trying to understand what I've done wrong. My build message spits out the error: '0' cannot be used as a function and highlights the line return ((5 / 9)(fahrenheit - 32)); within the function. Thanks in advance for any advice.
#include <iostream>
using namespace std;
double celsiusFunction(double fahrenheit);
int main()
{
double fahrenheitTemp;
fahrenheitTemp = celsiusFunction(99);
cout << fahrenheitTemp;
return 0;
}
double celsiusFunction(double fahrenheit)
{
return ((5 / 9)(fahrenheit - 32));
}
5 / 9 is 0, because both are integers and thus it's evaluated in integer arithmetic. Do this instead: 5.0 / 9.0 to get floating results.
You're not multiplying in the return statement, so the compiler interprets the second parentheses as a funciton call (that is, calling 5 / 9 with arguments fahrenheit - 32). This is of course nonsense. Do this:
return (5.0 / 9.0) * (fahrenheit - 32.0);
((5 / 9)(fahrenheit - 32))
\_____/\_______________/
1 2
2 is interpreted as a function call on 1. You forgot the multiplication:
((5 / 9) * (fahrenheit - 32))
You've forgotten the * operator
You should change return ((5 / 9)(fahrenheit - 32)); to
return ((5 / 9)*(fahrenheit - 32));
Add * after (5/9).
Because of missing * you are getting the error.
I have a template class that worked perfectly in Visual Studio 6 but fails in a more modern version.
template<double B, double C>
class MyClass
{
double k(double x) const
{
x = fabs(x);
if (x < 1.0)
return ((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - 0.33333333*B);
if (x < 2.0)
return ((-0.16666667*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x) + (1.3333333*B + 4.0*C);
return 0.0;
}
};
error C2993: 'double' : illegal type for non-type template parameter 'B'
I get it, the standard disallowed using double constants as template parameters and VC++ is finally conforming. But what should I use instead? The expression is evaluated repeatedly and is a true bottleneck, and I'd like the constants to be computed at compile time rather than run time.
The problem is that matching template arguments to the program-wide set of instantiations depends on exact equality, which based on the machine's particular representation of floating-point values may vary from platform to platform. (And since the compiler is not required to provide compile-time emulation of the target machine, may vary between compile time and runtime.)
The solution is to replace double with some other kind of unambiguous representation, such as ratios, fractions over a fixed amount such as a million, or your own floating-point style with integer mantissa and exponent.
In C++11, you could apply constexpr function evaluation, which in this case would eliminate the templates:
struct MyClass {
constexpr MyClass( double b, double c )
: b15( b * 1.5 ), b20( b * 2.0 ), b03( b * 0.33333333 ),
/* etc */
double b15, b20, b03, /* etc */
double k( double x ) const {
x = fabs(x);
if (x < 1.0)
return ((2.0 - b15 - C) * x*x*x) + ((-3.0 + b20 + C) * x*x) + (1.0 - b03);
/* etc */
}
};
This precomputes the constant subexpressions and effectively makes them runtime parameters to a single common function. But the parameter/member object accesses could slow it down.
If the function can always be inlined, that alternative will probably produce similar results to the original template at less effort.
if you use a constexpr function it should do everything you want at the minimal cost of having to use ?: operator since you can only have one return statement:
constexpr double k(double B, double C, double x) const
{
x = fabs(x);
return
(x < 1.0) ?
((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - 0.33333333*B)
: (x < 2.0) ?
((-0.16666667*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x)
+ 1.3333333*B + 4.0*C)
: 0.0;
}
but that requires that x is a literal (known at compiletime)
It also unfortunately requires that you do not use VS as per
http://msdn.microsoft.com/en-us/library/vstudio/hh567368.aspx
if x is a variable (changes at runtime) or you use VS:
just remove the constexpr keyword above. It is likely that most of the optimizations will occur simply from the fact that B and C are known at compiletime - check the asm to verify. (upgrading not only increases strictness, it also likely improved optimization)