improve mixed-precision vector operations

We now follow the same rules than for basic types, when performing
mixed-precision operations, e.g.:

  float3 + double3 -> double3
  double3 + float3 -> double3
  dot(float3, double3) -> double

In particular, e.g. swapping the arguments to arithmetic operations
now always yields to the same result type (before, float3 + double3
would not return the same type than double3 + float3).
This commit is contained in:
Mathias Agopian
2019-09-17 18:36:44 -07:00
committed by Mathias Agopian
parent 48b73ad998
commit cf950a8d6f
3 changed files with 75 additions and 55 deletions

View File

@@ -22,10 +22,9 @@
#include <stdint.h>
#include <sys/types.h>
#include <cmath>
#include <functional>
#include <limits>
#include <iostream>
#include <cmath> // for std:: namespace
#include <functional> // for appl() and map()
#include <iostream> // for operator<<
#include <math/compiler.h>
@@ -44,6 +43,15 @@ inline constexpr U max(U a, U b) noexcept {
return a > b ? a : b;
}
template<typename T, typename U>
struct arithmetic_result {
using type = decltype(std::declval<T>() + std::declval<U>());
};
template<typename T, typename U>
using arithmetic_result_t = typename arithmetic_result<T, U>::type;
/*
* No user serviceable parts here.
*
@@ -115,16 +123,20 @@ public:
/* The operators below handle operation between vectors of the same size
* but of a different element type.
*/
template<typename RT>
friend inline constexpr VECTOR<T> MATH_PURE operator+(VECTOR<T> lv, const VECTOR<RT>& rv) {
// don't pass lv by reference because we need a copy anyways
return lv += rv;
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res += rv;
return res;
}
template<typename RT>
friend inline constexpr VECTOR<T> MATH_PURE operator-(VECTOR<T> lv, const VECTOR<RT>& rv) {
// don't pass lv by reference because we need a copy anyways
return lv -= rv;
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res -= rv;
return res;
}
/* The operators below (which are not templates once this class is instanced,
@@ -200,16 +212,20 @@ public:
/* The operators below handle operation between vectors of the same size
* but of a different element type.
*/
template<typename RT>
friend inline constexpr VECTOR<T> MATH_PURE operator*(VECTOR<T> lv, const VECTOR<RT>& rv) {
// don't pass lv by reference because we need a copy anyways
return lv *= rv;
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<arithmetic_result_t<T, U>> res(lv);
res *= rv;
return res;
}
template<typename RT>
friend inline constexpr VECTOR<T> MATH_PURE operator/(VECTOR<T> lv, const VECTOR<RT>& rv) {
// don't pass lv by reference because we need a copy anyways
return lv /= rv;
template<typename U>
friend inline constexpr
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, const VECTOR<U>& rv) {
arithmetic_result_t<T, U> res(lv);
res /= rv;
return res;
}
/* The operators below (which are not templates once this class is instanced,
@@ -269,9 +285,9 @@ public:
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename RT>
template<typename U>
friend inline constexpr
bool MATH_PURE operator==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
bool MATH_PURE operator==(const VECTOR<T>& lv, const VECTOR<U>& rv) {
// w/ inlining we end-up with many branches that will pollute the BPU cache
MATH_NOUNROLL
for (size_t i = 0; i < lv.size(); i++) {
@@ -282,15 +298,15 @@ public:
return true;
}
template<typename RT>
template<typename U>
friend inline constexpr
bool MATH_PURE operator!=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
bool MATH_PURE operator!=(const VECTOR<T>& lv, const VECTOR<U>& rv) {
return !operator==(lv, rv);
}
template<typename RT>
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
VECTOR<bool> MATH_PURE equal(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] == rv[i];
@@ -298,9 +314,9 @@ public:
return r;
}
template<typename RT>
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
VECTOR<bool> MATH_PURE notEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] != rv[i];
@@ -308,9 +324,9 @@ public:
return r;
}
template<typename RT>
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
VECTOR<bool> MATH_PURE lessThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] < rv[i];
@@ -318,9 +334,9 @@ public:
return r;
}
template<typename RT>
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
VECTOR<bool> MATH_PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] <= rv[i];
@@ -328,9 +344,9 @@ public:
return r;
}
template<typename RT>
template<typename U>
friend inline constexpr
VECTOR<bool> MATH_PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
VECTOR<bool> MATH_PURE greaterThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r;
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] > rv[i];
@@ -338,9 +354,9 @@ public:
return r;
}
template<typename RT>
template<typename U>
friend inline
VECTOR<bool> MATH_PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
VECTOR<bool> MATH_PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
VECTOR<bool> r{};
for (size_t i = 0; i < lv.size(); i++) {
r[i] = lv[i] >= rv[i];
@@ -366,9 +382,10 @@ public:
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
template<typename RT>
friend constexpr inline T MATH_PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
T r(0);
template<typename U>
friend constexpr inline
arithmetic_result_t<T, U> MATH_PURE dot(const VECTOR<T>& lv, const VECTOR<U>& rv) {
arithmetic_result_t<T, U> r{};
for (size_t i = 0; i < lv.size(); i++) {
r += lv[i] * rv[i];
}
@@ -391,13 +408,15 @@ public:
return norm2(lv);
}
template<typename RT>
friend inline constexpr T MATH_PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
template<typename U>
friend inline constexpr
arithmetic_result_t<T, U> MATH_PURE distance(const VECTOR<T>& lv, const VECTOR<U>& rv) {
return length(rv - lv);
}
template<typename RT>
friend inline constexpr T MATH_PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
template<typename U>
friend inline constexpr
arithmetic_result_t<T, U> MATH_PURE distance2(const VECTOR<T>& lv, const VECTOR<U>& rv) {
return length2(rv - lv);
}
@@ -499,16 +518,16 @@ public:
}
friend inline constexpr T MATH_PURE max(const VECTOR<T>& v) {
T r(std::numeric_limits<T>::lowest());
for (size_t i = 0; i < v.size(); i++) {
T r(v[0]);
for (size_t i = 1; i < v.size(); i++) {
r = max(r, v[i]);
}
return r;
}
friend inline constexpr T MATH_PURE min(const VECTOR<T>& v) {
T r(std::numeric_limits<T>::max());
for (size_t i = 0; i < v.size(); i++) {
T r(v[0]);
for (size_t i = 1; i < v.size(); i++) {
r = min(r, v[i]);
}
return r;

View File

@@ -83,10 +83,10 @@ public:
constexpr TVec2(const TVec2<A>& v) : v{ T(v[0]), T(v[1]) } {}
// cross product works only on vectors of size 2 or 3
template<typename RT>
friend inline
constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) {
return value_type(u[0] * v[1] - u[1] * v[0]);
template<typename U>
friend inline constexpr
arithmetic_result_t<T, U> cross(const TVec2& u, const TVec2<U>& v) {
return u[0] * v[1] - u[1] * v[0];
}
};

View File

@@ -99,12 +99,13 @@ public:
constexpr TVec3(const TVec3<A>& v) : v{ T(v[0]), T(v[1]), T(v[2]) } {}
// cross product works only on vectors of size 3
template<typename RT>
friend inline constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) {
return TVec3(
template<typename U>
friend inline constexpr
TVec3<arithmetic_result_t<T, U>> cross(const TVec3& u, const TVec3<U>& v) {
return {
u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2],
u[0] * v[1] - u[1] * v[0]);
u[0] * v[1] - u[1] * v[0] };
}
};