blob: 1c45b5417b51665c21913b651bd922050c3bfbbf [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include <catch.hpp>
#include "rive/math/simd.hpp"
#include <limits>
namespace rive {
constexpr float kInf = std::numeric_limits<float>::infinity();
constexpr float kNaN = std::numeric_limits<float>::quiet_NaN();
// Verify the simd float types are IEEE 754 compliant for infinity and NaN.
TEST_CASE("ieee-compliance", "[simd]") {
float4 test = float4{1, -kInf, 1, 4} / float4{0, 2, kInf, 4};
CHECK(simd::all(test == float4{kInf, -kInf, 0, 1}));
// Inf * Inf == Inf
test = float4{kInf, -kInf, kInf, -kInf} * float4{kInf, kInf, -kInf, -kInf};
CHECK(simd::all(test == float4{kInf, -kInf, -kInf, kInf}));
// Inf/0 == Inf, 0/Inf == 0
test = float4{kInf, -kInf, 0, 0} / float4{0, 0, kInf, -kInf};
CHECK(simd::all(test == float4{kInf, -kInf, 0, 0}));
// Inf/Inf, 0/0, 0 * Inf, Inf - Inf == NaN
test = {kInf, 0, 0, kInf};
test.xy /= float2{kInf, 0};
test.z *= kInf;
test.w -= kInf;
for (int i = 0; i < 4; ++i) {
CHECK(std::isnan(test[i]));
}
// NaN always fails comparisons.
CHECK(!simd::any(test == test));
CHECK(simd::all(test != test));
CHECK(!simd::any(test <= test));
CHECK(!simd::any(test >= test));
CHECK(!simd::any(test < test));
CHECK(!simd::any(test > test));
// Inf + Inf == Inf, Inf + -Inf == NaN
test = float4{kInf, -kInf, kInf, -kInf} + float4{kInf, -kInf, -kInf, kInf};
CHECK(simd::all(test.xy == float2{kInf, -kInf}));
CHECK(!simd::any(test.zw == test.zw)); // NaN
}
// Check that ?: works on vector and scalar conditions.
TEST_CASE("ternary-operator", "[simd]") {
// Vector condition.
float4 f4 = int4{1, 2, 3, 4} < int4{4, 3, 2, 1} ? float4(-1) : 1.f;
CHECK(simd::all(f4 == float4{-1, -1, 1, 1}));
// In vector, -1 is true, 0 is false.
uint2 u2 = int2{0, -1} ? uint2{1, 2} : uint2{3, 4};
CHECK(simd::all(u2 == uint2{3, 2}));
// Scalar condition.
f4 = u2.x == u2.y ? float4{1, 2, 3, 4} : float4{5, 6, 7, 8};
CHECK(simd::all(f4 == float4{5, 6, 7, 8}));
}
// Check simd::min/max compliance.
TEST_CASE("min-max", "[simd]") {
float4 f4 = simd::min(float4{1, 2, 3, 4}, float4{4, 3, 2});
CHECK(simd::all(f4 == float4{1, 2, 2, 0}));
f4 = simd::max(float4{1, 2, 3, 4}, float4{4, 3, 2});
CHECK(simd::all(f4 == float4{4, 3, 3, 4}));
int2 i2 = simd::max(int2(-1), int2{-2});
CHECK(simd::all(i2 == int2{-1, 0}));
i2 = simd::min(int2(-1), int2{-2});
CHECK(simd::all(i2 == int2{-2, -1}));
// Infinity works as expected.
f4 = simd::min(float4{100, -kInf, -kInf, kInf}, float4{kInf, 100, kInf, -kInf});
CHECK(simd::all(f4 == float4{100, -kInf, -kInf, -kInf}));
f4 = simd::max(float4{100, -kInf, -kInf, kInf}, float4{kInf, 100, kInf, -kInf});
CHECK(simd::all(f4 == float4{kInf, 100, kInf, kInf}));
// If a or b is NaN, min returns whichever is not NaN.
f4 = simd::min(float4{1, kNaN, 2, kNaN}, float4{kNaN, 1, 1, kNaN});
CHECK(simd::all(f4.xyz == 1));
CHECK(std::isnan(f4.w));
f4 = simd::max(float4{1, kNaN, 2, kNaN}, float4{kNaN, 1, 1, kNaN});
CHECK(simd::all(f4.xyz == vec<3>{1, 1, 2}));
CHECK(std::isnan(f4.w));
// simd::min/max differs from std::min/max when the first argument is NaN.
CHECK(simd::min<float, 1>(kNaN, 1).x == 1);
CHECK(std::isnan(std::min<float>(kNaN, 1)));
CHECK(simd::max<float, 1>(kNaN, 1).x == 1);
CHECK(std::isnan(std::max<float>(kNaN, 1)));
// simd::min/max is equivalent std::min/max when the second argument is NaN.
CHECK(simd::min<float, 1>(1, kNaN).x == std::min<float>(1, kNaN));
CHECK(simd::max<float, 1>(1, kNaN).x == std::max<float>(1, kNaN));
}
// Check simd::abs.
TEST_CASE("abs", "[simd]") {
CHECK(simd::all(simd::abs(float4{-1, 2, -3, 4}) == float4{1, 2, 3, 4}));
CHECK(simd::all(simd::abs(float2{-5, 6}) == float2{5, 6}));
CHECK(simd::all(float4{-std::numeric_limits<float>::epsilon(),
-std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::max(),
-kInf} == float4{-std::numeric_limits<float>::epsilon(),
-std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::max(),
-kInf}
));
float2 nan2 = simd::abs(float2{kNaN, -kNaN});
CHECK(std::isnan(nan2.x));
CHECK(std::isnan(nan2.y));
CHECK(simd::all(simd::abs(int4{7, -8, 9, -10}) == int4{7, 8, 9, 10}));
// abs(INT_MIN) returns INT_MIN.
CHECK(
simd::all(simd::abs(int2{-std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::min()}) ==
int2{std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min()}));
}
} // namespace rive