improve vector operations when using implicit conversion
It used to be that operations e.g. like:
float3{} + double{} would be computed as
float3{} + float3{double{}} instead of
float3{} + double3{double{}}
I other words, when an implicit conversion was involved on the right
it would be converted to the left side’s type, possibly losing
precision.
Another problem was that swiping the operands could produce different
Results, e.g.:
float3{1} * 5.0 -> float3{5.0f}
5.0 * float3{1} -> double3{5.0}
This is no longer the case, now both expressions would return a double3.
Note:
float3 r{};
r *= 5;
Is now equivalent to:
r[0] *= 5;
r[1] *= 5;
r[2] *= 5;
Instead of before:
r[0] *= 5.0f;
r[1] *= 5.0f;
r[2] *= 5.0f;
This commit is contained in:
committed by
Mathias Agopian
parent
2f5927d531
commit
21acf53d3f
@@ -79,8 +79,8 @@ public:
|
||||
/* compound assignment from a another vector of the same size but different
|
||||
* element type.
|
||||
*/
|
||||
template<typename OTHER>
|
||||
constexpr VECTOR<T>& operator+=(const VECTOR<OTHER>& v) {
|
||||
template<typename U>
|
||||
constexpr VECTOR<T>& operator+=(const VECTOR<U>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] += v[i];
|
||||
@@ -88,8 +88,13 @@ public:
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<typename OTHER>
|
||||
constexpr VECTOR<T>& operator-=(const VECTOR<OTHER>& v) {
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
constexpr VECTOR<T>& operator+=(U v) {
|
||||
return operator+=(VECTOR<U>(v));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr VECTOR<T>& operator-=(const VECTOR<U>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] -= v[i];
|
||||
@@ -97,25 +102,9 @@ public:
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/* compound assignment from a another vector of the same type.
|
||||
* These operators can be used for implicit conversion and handle operations
|
||||
* like "vector *= scalar" by letting the compiler implicitly convert a scalar
|
||||
* to a vector (assuming the BASE<T> allows it).
|
||||
*/
|
||||
constexpr VECTOR<T>& operator+=(const VECTOR<T>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] += v[i];
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
constexpr VECTOR<T>& operator-=(const VECTOR<T>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] -= v[i];
|
||||
}
|
||||
return lhs;
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
constexpr VECTOR<T>& operator-=(U v) {
|
||||
return operator-=(VECTOR<U>(v));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -126,9 +115,6 @@ public:
|
||||
* (the first one, BASE<T> being known).
|
||||
*/
|
||||
|
||||
/* The operators below handle operation between vectors of the same size
|
||||
* but of a different element type.
|
||||
*/
|
||||
template<typename U>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, const VECTOR<U>& rv) {
|
||||
@@ -137,6 +123,12 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, U rv) {
|
||||
return lv + VECTOR<U>(rv);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, const VECTOR<U>& rv) {
|
||||
@@ -145,20 +137,10 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* The operators below (which are not templates once this class is instanced,
|
||||
* i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
|
||||
* These handle operations like "vector + scalar" and "scalar + vector" by
|
||||
* letting the compiler implicitly convert a scalar to a vector (assuming
|
||||
* the BASE<T> allows it).
|
||||
*/
|
||||
friend inline constexpr VECTOR<T> MATH_PURE operator+(VECTOR<T> lv, const VECTOR<T>& rv) {
|
||||
// don't pass lv by reference because we need a copy anyways
|
||||
return lv += rv;
|
||||
}
|
||||
|
||||
friend inline constexpr VECTOR<T> MATH_PURE operator-(VECTOR<T> lv, const VECTOR<T>& rv) {
|
||||
// don't pass lv by reference because we need a copy anyways
|
||||
return lv -= rv;
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, U rv) {
|
||||
return lv - VECTOR<U>(rv);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -168,8 +150,8 @@ public:
|
||||
/* compound assignment from a another vector of the same size but different
|
||||
* element type.
|
||||
*/
|
||||
template<typename OTHER>
|
||||
constexpr VECTOR<T>& operator*=(const VECTOR<OTHER>& v) {
|
||||
template<typename U>
|
||||
constexpr VECTOR<T>& operator*=(const VECTOR<U>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] *= v[i];
|
||||
@@ -177,8 +159,13 @@ public:
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<typename OTHER>
|
||||
constexpr VECTOR<T>& operator/=(const VECTOR<OTHER>& v) {
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
constexpr VECTOR<T>& operator*=(U v) {
|
||||
return operator*=(VECTOR<U>(v));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr VECTOR<T>& operator/=(const VECTOR<U>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] /= v[i];
|
||||
@@ -186,25 +173,9 @@ public:
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/* compound assignment from a another vector of the same type.
|
||||
* These operators can be used for implicit conversion and handle operations
|
||||
* like "vector *= scalar" by letting the compiler implicitly convert a scalar
|
||||
* to a vector (assuming the BASE<T> allows it).
|
||||
*/
|
||||
constexpr VECTOR<T>& operator*=(const VECTOR<T>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] *= v[i];
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
constexpr VECTOR<T>& operator/=(const VECTOR<T>& v) {
|
||||
VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
|
||||
for (size_t i = 0; i < lhs.size(); i++) {
|
||||
lhs[i] /= v[i];
|
||||
}
|
||||
return lhs;
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
constexpr VECTOR<T>& operator/=(U v) {
|
||||
return operator/=(VECTOR<U>(v));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -215,9 +186,6 @@ public:
|
||||
* (the first one, BASE<T> being known).
|
||||
*/
|
||||
|
||||
/* The operators below handle operation between vectors of the same size
|
||||
* but of a different element type.
|
||||
*/
|
||||
template<typename U>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, const VECTOR<U>& rv) {
|
||||
@@ -226,28 +194,26 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, U rv) {
|
||||
VECTOR<arithmetic_result_t<T, U>> res(lv);
|
||||
return res * VECTOR<U>(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);
|
||||
VECTOR<arithmetic_result_t<T, U>> res(lv);
|
||||
res /= rv;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* The operators below (which are not templates once this class is instanced,
|
||||
* i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
|
||||
* These handle operations like "vector * scalar" and "scalar * vector" by
|
||||
* letting the compiler implicitly convert a scalar to a vector (assuming
|
||||
* the BASE<T> allows it).
|
||||
*/
|
||||
friend inline constexpr VECTOR<T> MATH_PURE operator*(VECTOR<T> lv, const VECTOR<T>& rv) {
|
||||
// don't pass lv by reference because we need a copy anyways
|
||||
return lv *= rv;
|
||||
}
|
||||
|
||||
friend inline constexpr VECTOR<T> MATH_PURE operator/(VECTOR<T> lv, const VECTOR<T>& rv) {
|
||||
// don't pass lv by reference because we need a copy anyways
|
||||
return lv /= rv;
|
||||
template<typename U, typename = enable_if_arithmetic_t<U>>
|
||||
friend inline constexpr
|
||||
VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, U rv) {
|
||||
VECTOR<arithmetic_result_t<T, U>> res(lv);
|
||||
return res / VECTOR<U>(rv);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ TEST_F(VecTest, Constexpr) {
|
||||
constexpr float2 A2 = a;
|
||||
constexpr float2 B2 = { a, a };
|
||||
constexpr float2 C2 = A2;
|
||||
constexpr float2 E2 = A2 + 0.5f;
|
||||
constexpr float2 F2 = A2 + 0.5 - 1.0 + (1 + A2);
|
||||
constexpr float3 D2 = cross(A2, C2);
|
||||
|
||||
constexpr float3 A3 = a;
|
||||
@@ -64,7 +66,8 @@ TEST_F(VecTest, Constexpr) {
|
||||
|
||||
constexpr float4 S0 = A4 + B4;
|
||||
constexpr float4 S1 = C4 - D4;
|
||||
constexpr float4 S2 = A4 * a;
|
||||
constexpr float4 S2 = (a * A4) + (A4 * a);
|
||||
constexpr float4 S7 = (a / A4) + (A4 / a);
|
||||
constexpr float4 S3 = A4 * A4;
|
||||
constexpr float4 S4 = A4 / a;
|
||||
constexpr float4 S5 = A4 / A4;
|
||||
|
||||
Reference in New Issue
Block a user