|
-
October 13th, 2009, 06:17 PM
#1
type precedence in mathematical operations - how is it decided?
Hi,
When I overload, say, the + operator for a class, I usually return an instance of that class following the + operation. However in principle one might also return an instance of the type that has been added to the class (which might make sense in numerical computations if one wishes to avoid loss of precision). C++ appears to make this choice automatically. For example, try the following code (using the tr1 auto):
Code:
int a = 3;
double b = 3.9;
auto k = a + b;
cout << typeid(k).name() << endl; // k is a double
I was wondering if anybody could tell me how the compiler decides to make k a double rather than an int in this case, as I would like to similarly return the most precise type from an addition operator in a custom complex number class.
For example,
Code:
complex<int> a (3,3);
complex<double> b(4,4);
auto k = a + b;
// I would like k to be a complex<double>, not a complex<int>, but my overload of + returns the latter. I need to decide dynamically at compile time what the appropriate result is!
Is there any way to access information on the "type precedence" (for want of a better phrase) or do I need to go through all integral types and work out the correct precedence for the + operator? I gather I could put together a type_traits kind of class which typedefs the appropriate result from a binary operation on two different types, but is there an easier way to get the same effect?
Thanks!
-
October 13th, 2009, 06:21 PM
#2
Re: type precedence in mathematical operations - how is it decided?
It's possible that boost::result_of might be of some help here. I'm not all that familiar with it so I can't say for sure.
-
October 13th, 2009, 08:31 PM
#3
Re: type precedence in mathematical operations - how is it decided?
Hi Lindley,
Thanks for the suggestion, but to my understanding result_of uses a defined function with a statically defined return type, so it would just mean the problem moves to defining that function.
After a bit of experimentation I was able to use the typedef approach. I create a numeric_precedence class and specialized it for the numeric types, and within each specialization declared the desired return type from a binary mathematical operation on the two template parameters. My addition operators are defined as follows:
Code:
template <typename T1, typename T2>
inline complex<typename numerical_precedence<T1, T2>::type> operator+(T1 t, const complex<T2> & n)
{
typedef typename numerical_precedence<T1, T2>::type T;
return complex<T>(t + n.real(), n.imaginary());
}
template <typename T1, typename T2>
inline complex<typename numerical_precedence<T1, T2>::type> operator + (const complex<T1> & a, const complex<T2> & b)
{
typedef typename numerical_precedence<T1, T2>::type T;
return complex<T>(a.real() + b.real(), a.imaginary() + b.imaginary());
}
This gives the desired result. On the slim chance that anybody else might need to do this, the template specializations are provided below. I would still prefer a neater method if one exists!
Code:
#ifndef NUMERICAL_PRECEDENCE_HPP_
#define NUMERICAL_PRECEDENCE_HPP_
template <typename A, typename B> struct numerical_precedence;
template<> struct numerical_precedence<int, int> {typedef int type;};
template<> struct numerical_precedence<int, short int> {typedef int type;};
template<> struct numerical_precedence<int, long int> {typedef long int type;};
template<> struct numerical_precedence<int, long long int> {typedef long long int type;};
template<> struct numerical_precedence<int, unsigned int> {typedef unsigned int type;};
template<> struct numerical_precedence<int, short unsigned int> {typedef int type;};
template<> struct numerical_precedence<int, long unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<int, float> {typedef float type;};
template<> struct numerical_precedence<int, double> {typedef double type;};
template<> struct numerical_precedence<int, long double> {typedef long double type;};
template<> struct numerical_precedence<short int, int> {typedef int type;};
template<> struct numerical_precedence<short int, short int> {typedef short int type;};
template<> struct numerical_precedence<short int, long int> {typedef long int type;};
template<> struct numerical_precedence<short int, long long int> {typedef long long int type;};
template<> struct numerical_precedence<short int, unsigned int> {typedef unsigned int type;};
template<> struct numerical_precedence<short int, short unsigned int> {typedef int type;};
template<> struct numerical_precedence<short int, long unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<short int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<short int, float> {typedef float type;};
template<> struct numerical_precedence<short int, double> {typedef double type;};
template<> struct numerical_precedence<short int, long double> {typedef long double type;};
template<> struct numerical_precedence<long int, int> {typedef long int type;};
template<> struct numerical_precedence<long int, short int> {typedef long int type;};
template<> struct numerical_precedence<long int, long int> {typedef long int type;};
template<> struct numerical_precedence<long int, long long int> {typedef long long int type;};
template<> struct numerical_precedence<long int, unsigned int> {typedef long int type;};
template<> struct numerical_precedence<long int, short unsigned int> {typedef long int type;};
template<> struct numerical_precedence<long int, long unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long int, float> {typedef float type;};
template<> struct numerical_precedence<long int, double> {typedef double type;};
template<> struct numerical_precedence<long int, long double> {typedef long double type;};
template<> struct numerical_precedence<long long int, int> {typedef long long int type;};
template<> struct numerical_precedence<long long int, short int> {typedef long long int type;};
template<> struct numerical_precedence<long long int, long int> {typedef long long int type;};
template<> struct numerical_precedence<long long int, long long int> {typedef long long int type;};
template<> struct numerical_precedence<long long int, unsigned int> {typedef long long int type;};
template<> struct numerical_precedence<long long int, short unsigned int> {typedef long long int type;};
template<> struct numerical_precedence<long long int, long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long int, float> {typedef float type;};
template<> struct numerical_precedence<long long int, double> {typedef double type;};
template<> struct numerical_precedence<long long int, long double> {typedef long double type;};
template<> struct numerical_precedence<unsigned int, int> {typedef unsigned int type;};
template<> struct numerical_precedence<unsigned int, short int> {typedef unsigned int type;};
template<> struct numerical_precedence<unsigned int, long int> {typedef long int type;};
template<> struct numerical_precedence<unsigned int, long long int> {typedef long long int type;};
template<> struct numerical_precedence<unsigned int, unsigned int> {typedef unsigned int type;};
template<> struct numerical_precedence<unsigned int, short unsigned int> {typedef unsigned int type;};
template<> struct numerical_precedence<unsigned int, long unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<unsigned int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<unsigned int, float> {typedef float type;};
template<> struct numerical_precedence<unsigned int, double> {typedef double type;};
template<> struct numerical_precedence<unsigned int, long double> {typedef long double type;};
template<> struct numerical_precedence<short unsigned int, int> {typedef int type;};
template<> struct numerical_precedence<short unsigned int, short int> {typedef int type;};
template<> struct numerical_precedence<short unsigned int, long int> {typedef long int type;};
template<> struct numerical_precedence<short unsigned int, long long int> {typedef long long int type;};
template<> struct numerical_precedence<short unsigned int, unsigned int> {typedef unsigned int type;};
template<> struct numerical_precedence<short unsigned int, short unsigned int> {typedef short unsigned int type;};
template<> struct numerical_precedence<short unsigned int, long unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<short unsigned int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<short unsigned int, float> {typedef float type;};
template<> struct numerical_precedence<short unsigned int, double> {typedef double type;};
template<> struct numerical_precedence<short unsigned int, long double> {typedef long double type;};
template<> struct numerical_precedence<long unsigned int, int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, short int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, long int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, long long int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, short unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, long unsigned int> {typedef long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long unsigned int, float> {typedef float type;};
template<> struct numerical_precedence<long unsigned int, double> {typedef double type;};
template<> struct numerical_precedence<long unsigned int, long double> {typedef long double type;};
template<> struct numerical_precedence<long long unsigned int, int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, short int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, long int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, long long int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, short unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, long long unsigned int> {typedef long long unsigned int type;};
template<> struct numerical_precedence<long long unsigned int, float> {typedef float type;};
template<> struct numerical_precedence<long long unsigned int, double> {typedef double type;};
template<> struct numerical_precedence<long long unsigned int, long double> {typedef long double type;};
template<> struct numerical_precedence<float, int> {typedef float type;};
template<> struct numerical_precedence<float, short int> {typedef float type;};
template<> struct numerical_precedence<float, long int> {typedef float type;};
template<> struct numerical_precedence<float, long long int> {typedef float type;};
template<> struct numerical_precedence<float, unsigned int> {typedef float type;};
template<> struct numerical_precedence<float, short unsigned int> {typedef float type;};
template<> struct numerical_precedence<float, long unsigned int> {typedef float type;};
template<> struct numerical_precedence<float, long long unsigned int> {typedef float type;};
template<> struct numerical_precedence<float, float> {typedef float type;};
template<> struct numerical_precedence<float, double> {typedef double type;};
template<> struct numerical_precedence<float, long double> {typedef long double type;};
template<> struct numerical_precedence<double, int> {typedef double type;};
template<> struct numerical_precedence<double, short int> {typedef double type;};
template<> struct numerical_precedence<double, long int> {typedef double type;};
template<> struct numerical_precedence<double, long long int> {typedef double type;};
template<> struct numerical_precedence<double, unsigned int> {typedef double type;};
template<> struct numerical_precedence<double, short unsigned int> {typedef double type;};
template<> struct numerical_precedence<double, long unsigned int> {typedef double type;};
template<> struct numerical_precedence<double, long long unsigned int> {typedef double type;};
template<> struct numerical_precedence<double, float> {typedef double type;};
template<> struct numerical_precedence<double, double> {typedef double type;};
template<> struct numerical_precedence<double, long double> {typedef long double type;};
template<> struct numerical_precedence<long double, int> {typedef long double type;};
template<> struct numerical_precedence<long double, short int> {typedef long double type;};
template<> struct numerical_precedence<long double, long int> {typedef long double type;};
template<> struct numerical_precedence<long double, long long int> {typedef long double type;};
template<> struct numerical_precedence<long double, unsigned int> {typedef long double type;};
template<> struct numerical_precedence<long double, short unsigned int> {typedef long double type;};
template<> struct numerical_precedence<long double, long unsigned int> {typedef long double type;};
template<> struct numerical_precedence<long double, long long unsigned int> {typedef long double type;};
template<> struct numerical_precedence<long double, float> {typedef long double type;};
template<> struct numerical_precedence<long double, double> {typedef long double type;};
template<> struct numerical_precedence<long double, long double> {typedef long double type;};
#endif // NUMERICAL_PRECEDENCE_HPP_
-
October 14th, 2009, 02:36 AM
#4
Re: type precedence in mathematical operations - how is it decided?
I have not thought about it much, but perhaps you can use something from the boost library.
http://www.boost.org/doc/libs/1_40_0...its_class.html
Code:
template <typename T1, typename T2>
inline complex<typename boost::numeric::conversion_traits<T1,T2>::supertype> operator + (const complex<T1> & a, const complex<T2> & b)
{
return complex<typename boost::numeric::conversion_traits<T1,T2>::supertype>(a.real() + b.real(), a.imaginary() + b.imaginary());
}
The problem with this is you seem to want to always promote signed unsigned combinations to unsigned.
Wakeup in the morning and kick the day in the teeth!! Or something like that.
"i don't want to write leak free code or most efficient code, like others traditional (so called expert) coders do."
-
October 14th, 2009, 03:18 AM
#5
Re: type precedence in mathematical operations - how is it decided?
PHP Code:
void myFunction(int) { std::cout << "int!" << std::endl; } void myFunction(double) { std::cout << "double!" << std::endl; }
int main() { int a; double b; myFunction(a+b); }
The output is double. If it was anything else than double or int, it would not have compiled (ambiguous).
From memory, your int will be promoted to double before calling the operator+.
There are no mixed type operator+ in c++, the arguments get promoted before the call. As a general rule, they get promoted "up", if the operator exists (90% sure ). But this is only for built in operators.
By up, I mean char < short < int < long < float < double < long double
This will not work, however
PHP Code:
void myFunction(int, int) { std::cout << "int!" << std::endl; } void myFunction(double, double) { std::cout << "double!" << std::endl; }
int main() { int a; double b; myFunction(a,b); }
Because myFunction is not a built in operator (as oposed to operator+(int, int) or operator(double, double))
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|