diff --git a/fixed_lib/include/fixedmath/detail/common.h b/fixed_lib/include/fixedmath/detail/common.h index e02d18a..d541799 100644 --- a/fixed_lib/include/fixedmath/detail/common.h +++ b/fixed_lib/include/fixedmath/detail/common.h @@ -1,25 +1,29 @@ #pragma once #include "../types.h" -#include - -#define FIXEDMATH_PUBLIC gnu::visibility("default") +#include namespace fixedmath::inline v2::detail { template [[gnu::const, gnu::always_inline]] -constexpr auto unsigned_shift_left_signed(fixed_internal value) noexcept -> fixed_internal +constexpr auto unsigned_shift_left_signed(std::signed_integral auto value) noexcept -> fixed_internal { - using unsigned_internal = std::make_unsigned_t; return static_cast( - (static_cast(value) << digits) - | (static_cast(value) & (unsigned_internal(1) << 63u)) + (static_cast(value) << digits) + | (static_cast(value) & (fixed_internal_unsigned(1) << 63u)) ); } -template +template +[[nodiscard, gnu::const, gnu::always_inline]] +constexpr auto unsigned_shift_left_unsigned(std::unsigned_integral auto value) noexcept -> fixed_internal + { + return static_cast(static_cast(value) << digits); + } + +template [[nodiscard, gnu::const, gnu::always_inline]] constexpr auto promote_type_to_signed(value_type value) noexcept { @@ -32,17 +36,9 @@ constexpr auto promote_type_to_signed(value_type value) noexcept } } -template -[[nodiscard, gnu::const, gnu::always_inline]] -constexpr auto unsigned_shift_left_unsigned(fixed_internal value) noexcept -> fixed_internal - { - using unsigned_internal = std::make_unsigned_t; - return static_cast(static_cast(value) << digits); - } - [[nodiscard, gnu::const, gnu::always_inline]] ///\returns the highest power of 4 that is less than or equal to \ref value -constexpr auto highest_pwr4_clz(fixed_internal_unsigned value) noexcept -> fixed_internal +constexpr auto highest_pwr4_clz(concepts::internal_unsigned auto value) noexcept -> fixed_internal { if(value != 0) [[likely]] { @@ -58,7 +54,7 @@ constexpr auto highest_pwr4_clz(fixed_internal_unsigned value) noexcept -> fixed } [[nodiscard, gnu::const, gnu::always_inline]] -constexpr auto highest_pwr4(fixed_internal_unsigned value) noexcept -> fixed_internal +constexpr auto highest_pwr4(std::unsigned_integral auto value) noexcept -> fixed_internal { // one starts at the highest power of four <= than the argument. fixed_internal_unsigned pwr4{1ll << 62}; // second-to-top bit set @@ -70,27 +66,27 @@ constexpr auto highest_pwr4(fixed_internal_unsigned value) noexcept -> fixed_int template [[nodiscard, gnu::const, gnu::always_inline]] -constexpr auto mul_(fixed_internal x, fixed_internal y) noexcept -> fixed_internal +constexpr auto mul_(concepts::internal auto x, concepts::internal auto y) noexcept -> fixed_internal { return (x * y) >> precision; } template [[nodiscard, gnu::const, gnu::always_inline]] -constexpr auto div_(fixed_internal x, fixed_internal y) noexcept -> fixed_internal +constexpr auto div_(concepts::internal auto x, concepts::internal auto y) noexcept -> fixed_internal { return (x << precision) / y; } template [[nodiscard, gnu::const, gnu::always_inline]] -constexpr auto fix_(fixed_internal x) noexcept -> fixed_internal +constexpr auto fix_(std::integral auto x) noexcept -> fixed_internal { - return (x << precision); + return fixed_internal(x) << precision; } [[nodiscard, gnu::const, gnu::always_inline]] -constexpr auto set_sign(bool sign_, fixed_internal result) -> fixed_t +constexpr auto set_sign(bool sign_, concepts::internal auto result) -> fixed_t { if(!sign_) return as_fixed(result); @@ -104,5 +100,5 @@ constexpr void swap(T & a, T & b) noexcept a = b; b = temp; } - } // namespace fixedmath::detail + } // namespace fixedmath::inline v2::detail diff --git a/fixed_lib/include/fixedmath/detail/type_traits.h b/fixed_lib/include/fixedmath/detail/type_traits.h index 6c02b2c..ae44ba5 100644 --- a/fixed_lib/include/fixedmath/detail/type_traits.h +++ b/fixed_lib/include/fixedmath/detail/type_traits.h @@ -37,7 +37,6 @@ using promote_to_signed_t = signed_type_by_size_t<(sizeof(T) << 1)>; template inline constexpr bool is_integral_v = std::is_integral_v; - template inline constexpr bool is_fixed_point_v = std::is_same_v; @@ -70,6 +69,11 @@ inline constexpr bool one_of_is_double_v = std::is_same_v || std::is_ namespace fixedmath::inline v2::concepts { using std::integral; +template +concept internal_unsigned = std::same_as; + +template +concept internal = std::same_as; template concept fixed_point = typetraits::is_fixed_point_v; @@ -91,6 +95,7 @@ concept arithmetic_and_one_is_fixed = typetraits::is_arithmetic_and_one_is_fixed template concept one_of_is_double = typetraits::one_of_is_double_v; + } // namespace fixedmath::inline v2::concepts namespace fixedmath::inline v2::detail diff --git a/fixed_lib/include/fixedmath/math.h b/fixed_lib/include/fixedmath/math.h index ce8a921..45eb3fd 100644 --- a/fixed_lib/include/fixedmath/math.h +++ b/fixed_lib/include/fixedmath/math.h @@ -226,7 +226,7 @@ struct subtract_t template requires concepts::arithmetic_and_one_is_fixed [[nodiscard, gnu::const, gnu::always_inline]] - constexpr auto operator()(supported_type1 lh, supported_type2 rh) noexcept + static constexpr auto operator()(supported_type1 lh, supported_type2 rh) noexcept { if constexpr(typetraits::one_of_is_double_v) return detail::promote_to_double(lh) - detail::promote_to_double(rh); diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 5f32678..12e4a4c 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -53,7 +53,7 @@ endfunction() add_fixedmath_ut( fixed_construction_ut ) add_fixedmath_ut( type_convertions_ut ) add_fixedmath_ut( addition_ut ) - +add_fixedmath_ut( subtraction_ut ) # add_compile_test( addition ) # add_compile_test( substraction ) diff --git a/unit_tests/addition_ut.cc b/unit_tests/addition_ut.cc index b35153c..40a5c02 100644 --- a/unit_tests/addition_ut.cc +++ b/unit_tests/addition_ut.cc @@ -42,6 +42,26 @@ int main() expect_neq(limits_::lowest() + 1, limits_::quiet_NaN()); expect_neq(as_fixed(limits_::max().v - 65536) + 1_fix, limits_::quiet_NaN()); + + expect( test_resulting_type( int64_t(1) + 1_fix ) ); + expect( test_resulting_type( 1_fix + int64_t(1) ) ); + expect( test_resulting_type( uint64_t(1) + 1_fix ) ); + expect( test_resulting_type( 1_fix + uint64_t(1) ) ); + expect( test_resulting_type( 1_fix + 1_fix ) ); + expect( test_resulting_type( 1 + 1_fix ) ); + expect( test_resulting_type( 1_fix + 1 ) ); + expect( test_resulting_type( int16_t(1) + 1_fix ) ); + expect( test_resulting_type( 1_fix + int16_t(1) ) ); + expect( test_resulting_type( uint16_t(1) + 1_fix ) ); + expect( test_resulting_type( 1_fix + uint16_t(1) ) ); + expect( test_resulting_type( int8_t(1) + 1_fix ) ); + expect( test_resulting_type( 1_fix + int8_t(1) ) ); + expect( test_resulting_type( uint8_t(1) + 1_fix ) ); + expect( test_resulting_type( 1_fix + uint8_t(1) ) ); + expect( test_resulting_type( 1.f + 1_fix ) ); + expect( test_resulting_type( 1_fix + 1.f ) ); + expect( test_resulting_type( 1. + 1_fix ) ); + expect( test_resulting_type( 1_fix + 1. ) ); return {}; }; result |= run_constexpr_test(fn_tmpl); diff --git a/unit_tests/subtraction_ut.cc b/unit_tests/subtraction_ut.cc new file mode 100644 index 0000000..b5c7e42 --- /dev/null +++ b/unit_tests/subtraction_ut.cc @@ -0,0 +1,61 @@ +#include +#include + +using boost::ut::operator""_test; +using namespace metatests; +using namespace fixedmath; + +int main() + { + test_result result; + using F = fixedmath::fixed_internal; + "addition"_test = [&result] + { + auto fn_tmpl = [] -> metatests::test_result + { + expect_eq(0.2_fix - 1.2_fix, -1_fix); + expect_eq(3.2_fix - 1.2_fix, 2_fix); + expect_eq(-4.2_fix - 1.2_fix, -5.4_fix); + expect_eq(-4.2_fix + 1.2_fix, -3_fix); + + static constexpr auto approx{std::numeric_limits::epsilon()}; + expect_approx(1_fix - 1., 1. - 1., approx); + expect_approx(2_fix - 1., 2. - 1., approx); + expect_approx(-2_fix - 1., -2. - 1., approx); + expect_approx(-2_fix + 1., -2. + 1., approx); + + expect_eq(-10.5_fix - uint8_t(10), -20.5_fix); + expect_eq(-10.5_fix - uint16_t(10), -20.5_fix); + expect_eq(-10.5_fix - uint32_t(10), -20.5_fix); + expect_eq(-10.5_fix - uint64_t(10), -20.5_fix); + expect_eq(uint8_t(10) - 10.5_fix, -.5_fix); + expect_eq(uint16_t(10) - 10.5_fix, -.5_fix); + expect_eq(uint32_t(10) - 10.5_fix, -.5_fix); + expect_eq(uint64_t(10) - 10.5_fix, -.5_fix); + expect_neq(limits_::max() - 1, limits_::quiet_NaN()); + + expect(test_resulting_type(int64_t(1) - 1_fix)); + expect(test_resulting_type(1_fix - int64_t(1))); + expect(test_resulting_type(uint64_t(1) - 1_fix)); + expect(test_resulting_type(1_fix - uint64_t(1))); + expect(test_resulting_type(1_fix - 1_fix)); + expect(test_resulting_type(1 - 1_fix)); + expect(test_resulting_type(1_fix - 1)); + expect(test_resulting_type(int16_t(1) - 1_fix)); + expect(test_resulting_type(1_fix - int16_t(1))); + expect(test_resulting_type(uint16_t(1) - 1_fix)); + expect(test_resulting_type(1_fix - uint16_t(1))); + expect(test_resulting_type(int8_t(1) - 1_fix)); + expect(test_resulting_type(1_fix - int8_t(1))); + expect(test_resulting_type(uint8_t(1) - 1_fix)); + expect(test_resulting_type(1_fix - uint8_t(1))); + expect(test_resulting_type(1.f - 1_fix)); + expect(test_resulting_type(1_fix - 1.f)); + expect(test_resulting_type(1. - 1_fix)); + expect(test_resulting_type(1_fix - 1.)); + return {}; + }; + result |= run_constexpr_test(fn_tmpl); + result |= run_consteval_test(fn_tmpl); + }; + }