summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compat/math.hpp85
-rw-r--r--src/critter.cpp6
-rw-r--r--test/app.hpp3
-rw-r--r--test/magnum-math.cpp (renamed from test/const-math.cpp)7
-rw-r--r--test/main.cpp3
-rw-r--r--test/math.cpp105
6 files changed, 163 insertions, 46 deletions
diff --git a/compat/math.hpp b/compat/math.hpp
index 8df83cc3..0afa3c8f 100644
--- a/compat/math.hpp
+++ b/compat/math.hpp
@@ -1,52 +1,57 @@
#pragma once
+#include <cstdlib>
#include <bit>
#include <cmath>
-namespace floormat::math::detail {
-
-constexpr double sqrt_newton_raphson(double x, double curr, double prev)
-{
- return curr == prev
- ? curr
- : sqrt_newton_raphson(x, 0.5 * (curr + x / curr), curr);
-}
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wfloat-equal"
+#endif
-template<typename T> requires std::is_floating_point_v<T> struct float_constants;
+namespace floormat::math::detail {
-template<>
-struct float_constants<double>
-{
- static constexpr auto quiet_nan = std::bit_cast<double>(uint64_t(0x7FF8000000000000ULL));
- static constexpr auto positive_infinity = std::bit_cast<double>(uint64_t(0x7FF0000000000000ULL));
- static constexpr auto negative_infinity = std::bit_cast<double>(uint64_t(0xFFF0000000000000ULL));
-};
+template<typename T> struct int_type_for_;
-template<>
-struct float_constants<float>
-{
- static constexpr auto quiet_nan = std::bit_cast<float>(uint32_t(0x7FC00000U));
- static constexpr auto positive_infinity = std::bit_cast<float>(uint32_t(0x7F800000U));
- static constexpr auto negative_infinity = std::bit_cast<float>(uint32_t(0xFF800000U));
-};
+template<> struct int_type_for_<float> { using type = int32_t; };
+template<> struct int_type_for_<double> { using type = int64_t; };
+template<typename T> using int_type_for = typename int_type_for_<T>::type;
} // namespace floormat::math::detail
namespace floormat::math {
template<typename T>
-constexpr inline T sqrt(T x)
+constexpr inline T abs(T x)
+requires std::is_arithmetic_v<T>
+{
+ static_assert(std::is_floating_point_v<T> ||
+ std::is_integral_v<T> && std::is_signed_v<T>);
+ return x < T{0} ? -x : x;
+}
+
+template <typename T>
+requires std::is_arithmetic_v<T>
+constexpr inline T sgn(T val)
+{
+ return T(T{0} < val) - T(val < T{0});
+}
+
+template<typename T>
+constexpr inline T sqrt(T x0)
requires std::is_floating_point_v<T>
{
if (std::is_constant_evaluated())
{
- using K = detail::float_constants<T>;
- return x >= 0 && x < K::positive_infinity
- ? T(detail::sqrt_newton_raphson(double(x), double(x), 0))
- : K::quiet_nan;
+ auto x = x0, prev = T{0};
+ while (x != prev)
+ {
+ prev = x;
+ x = T(0.5) * (x + x0 / x);
+ }
+ return x;
}
else
- return std::sqrt(x);
+ return std::sqrt(x0);
}
template<typename T>
@@ -62,14 +67,26 @@ constexpr inline T ceil(T x)
{
if (std::is_constant_evaluated())
{
- const auto x0 = int64_t(x);
- if (x > x0)
- return T(x0 + int64_t(1));
- else
- return x0;
+ using int_ = detail::int_type_for<T>;
+ const auto x0 = int_(x);
+ return x0 + int_{1} * (x > x0);
}
else
return std::ceil(x);
}
+template<typename T>
+requires std::is_floating_point_v<T>
+constexpr inline T floor(T x)
+{
+ if (std::is_constant_evaluated())
+ {
+ using int_ = detail::int_type_for<T>;
+ const auto x0 = int_(x);
+ return x0 - int_{1} * (x < T{0} && x != x0);
+ }
+ else
+ return std::floor(x);
+}
+
} // namespace floormat::math
diff --git a/src/critter.cpp b/src/critter.cpp
index c0e17d44..f8971710 100644
--- a/src/critter.cpp
+++ b/src/critter.cpp
@@ -14,8 +14,6 @@ namespace floormat {
namespace {
-template <typename T> constexpr T sgn(T val) { return T(T(0) < val) - T(val < T(0)); }
-
constexpr auto vector_length(Vector2 vec)
{
return math::sqrt(Math::dot(vec, vec));
@@ -125,7 +123,7 @@ constexpr Vector2 move_vec(Vector2i vec)
{
const int left_right = vec[0], top_bottom = vec[1];
constexpr auto c = move_speed * frame_time;
- auto dir = Vector2((float)sgn(left_right), (float)sgn(top_bottom));
+ auto dir = Vector2((float)math::sgn(left_right), (float)math::sgn(top_bottom));
auto inv_norm = 1.f/math::sqrt(Math::dot(dir, dir));
return c * dir * inv_norm;
}
@@ -197,7 +195,7 @@ void critter::update(size_t i, float dt)
auto vec = move_vecs[j];
constexpr auto frac = 65535u;
constexpr auto inv_frac = 1.f / (float)frac;
- const auto sign_vec = Vector2(sgn(vec[0]), sgn(vec[1]));
+ const auto sign_vec = Vector2(math::sgn(vec[0]), math::sgn(vec[1]));
auto offset_ = vec + Vector2(offset_frac) * sign_vec * inv_frac;
offset_frac = Vector2us(Vector2(std::fabs(std::fmod(offset_[0], 1.f)), std::fabs(std::fmod(offset_[1], 1.f))) * frac);
auto off_i = Vector2i(offset_);
diff --git a/test/app.hpp b/test/app.hpp
index f3e57da2..db1d11a9 100644
--- a/test/app.hpp
+++ b/test/app.hpp
@@ -33,7 +33,8 @@ struct test_app final : private FM_APPLICATION
static void test_json();
static void test_tile_iter();
- static void test_const_math();
+ static void test_magnum_math();
+ static void test_math();
static void test_serializer_1();
static void test_serializer_2();
static void test_entity();
diff --git a/test/const-math.cpp b/test/magnum-math.cpp
index 114af28f..d9dc7bb6 100644
--- a/test/const-math.cpp
+++ b/test/magnum-math.cpp
@@ -6,8 +6,6 @@
#include <Magnum/Math/Vector3.h>
#include <Magnum/Math/Vector4.h>
-#if defined CORRADE_CONSTEXPR14_
-
#ifdef __GNUG__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
@@ -64,7 +62,7 @@ constexpr bool compile_tests()
} // namespace
-void test_app::test_const_math()
+void test_app::test_magnum_math()
{
static_assert(compile_tests());
}
@@ -74,6 +72,3 @@ void test_app::test_const_math()
#ifdef __GNUG__
# pragma GCC diagnostic pop
#endif
-#else
-void floormat::test_app::test_const_math() {}
-#endif
diff --git a/test/main.cpp b/test/main.cpp
index fe07ce79..131e8452 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -23,13 +23,14 @@ int test_app::exec()
{
test_json();
test_tile_iter();
- test_const_math();
+ test_magnum_math();
test_entity();
test_loader();
test_bitmask();
test_serializer_1();
test_serializer_2();
test_path_search();
+ test_math();
zzz_test_misc();
return 0;
diff --git a/test/math.cpp b/test/math.cpp
new file mode 100644
index 00000000..d70a64fb
--- /dev/null
+++ b/test/math.cpp
@@ -0,0 +1,105 @@
+#include "app.hpp"
+#include "compat/math.hpp"
+
+namespace floormat {
+
+namespace {
+
+constexpr bool test_double_sqrt()
+{
+ using F = double;
+ constexpr auto eps = F(1e-11);
+
+ static_assert(math::abs(math::sqrt((F)3) - (F)1.73205080757) < eps);
+ return true;
+}
+
+template<typename F>
+bool test_sqrt()
+{
+ constexpr auto eps = F(1e-11);
+ constexpr auto test = [](double x)
+ {
+ auto x_ = (F)x;
+ auto y1 = math::sqrt(x_);
+ auto y2 = std::sqrt(x_);
+ return math::abs(y1 - y2) < eps;
+ };
+
+ static_assert(math::abs(math::sqrt((F)0) - (F)0) < eps);
+ static_assert(math::abs(math::sqrt((F)0.5) - (F)0.70710678118) < eps);
+ static_assert(math::abs(math::sqrt((F)1e-8) - (F)0.0001) < eps);
+ static_assert(math::abs(math::sqrt((F)2) - (F)1.41421356237) < eps);
+ static_assert(math::abs(math::sqrt((F)3) - (F)1.73205080757) < (F)1e-6);
+
+ static_assert(math::sqrt((F)0) == (F)0);
+ static_assert(math::sqrt((F)1) == (F)1);
+ static_assert(math::sqrt((F)4) == (F)2);
+ static_assert(math::sqrt((F)9) == (F)3);
+ static_assert(math::sqrt((F)36) == (F)6);
+
+ fm_assert(test(0));
+ fm_assert(test(0.5));
+ fm_assert(test(1.5));
+ fm_assert(test(2));
+ fm_assert(test(3));
+ fm_assert(test(0));
+ fm_assert(test(42));
+ fm_assert(test(41.5));
+ fm_assert(test(1e-8));
+ fm_assert(test(1e8));
+ fm_assert(test(1.23456789));
+ fm_assert(test(1e10 + 1.23456789));
+ fm_assert(test(531610));
+ fm_assert(test(1e10));
+ fm_assert(test(1 << 20));
+
+ return true;
+}
+
+template<typename F>
+constexpr bool test_floor()
+{
+ fm_assert(math::floor((F)-1.5) == -2);
+ fm_assert(math::floor((F)0) == 0);
+ fm_assert(math::floor((F)1) == 1);
+ fm_assert(math::floor((F)-1) == -1);
+ fm_assert(math::floor((F)-2) == -2);
+ fm_assert(math::floor((F)1.0000001) == 1);
+ fm_assert(math::floor((F)-1.000001) == -2);
+ fm_assert(math::floor((F)1e-8) == 0);
+ fm_assert(math::floor((F)-1e-8) == -1);
+
+ return true;
+}
+
+template<typename F>
+constexpr bool test_ceil()
+{
+ fm_assert(math::ceil((F)-1.5) == -1);
+ fm_assert(math::ceil((F)0) == 0);
+ fm_assert(math::ceil((F)1) == 1);
+ fm_assert(math::ceil((F)-1) == -1);
+ fm_assert(math::ceil((F)-2) == -2);
+ fm_assert(math::ceil((F)1.0000001) == 2);
+ fm_assert(math::ceil((F)-1.000001) == -1);
+ fm_assert(math::ceil((F)1e-8) == 1);
+ fm_assert(math::ceil((F)-1e-8) == 0);
+
+ return true;
+}
+
+} // namespace
+
+void test_app::test_math()
+{
+ static_assert(test_double_sqrt());
+ fm_assert(test_sqrt<float>());
+ fm_assert(test_sqrt<double>());
+ static_assert(test_floor<float>());
+ static_assert(test_floor<double>());
+ static_assert(test_ceil<float>());
+ static_assert(test_ceil<double>());
+}
+
+} // namespace floormat