diff options
| -rw-r--r-- | include/fixed_point.h | 1 | ||||
| -rw-r--r-- | src/fixed_point.cpp | 29 | ||||
| -rw-r--r-- | tests/test_fixed_point.cpp | 46 |
3 files changed, 70 insertions, 6 deletions
diff --git a/include/fixed_point.h b/include/fixed_point.h index ff8e2b3..9df6376 100644 --- a/include/fixed_point.h +++ b/include/fixed_point.h @@ -15,6 +15,7 @@ struct fp_t { fp_t operator+(const fp_t& other) const; fp_t operator-(const fp_t& other) const; fp_t operator*(const fp_t& other) const; + fp_t operator/(const fp_t& other) const; bool operator==(const fp_t& other) const; }; diff --git a/src/fixed_point.cpp b/src/fixed_point.cpp index 847a5f7..8b4b937 100644 --- a/src/fixed_point.cpp +++ b/src/fixed_point.cpp @@ -1,22 +1,39 @@ #include "fixed_point.h" +#include <cstdlib> +#include <cstdio> fp_t fp_t::operator+(const fp_t& other) const { - return {(int32_t)((uint32_t)raw + (uint32_t)other.raw)}; + return {(std::int32_t)((std::uint32_t)raw + (std::uint32_t)other.raw)}; } fp_t fp_t::operator-(const fp_t& other) const { - return {(int32_t)((uint32_t)raw - (uint32_t)other.raw)}; + return {(std::int32_t)((std::uint32_t)raw - (std::uint32_t)other.raw)}; } fp_t fp_t::operator*(const fp_t& other) const { - int32_t result; - int64_t temp; - temp = (int64_t)raw * (int64_t)other.raw; + std::int32_t result; + std::int64_t temp; + temp = (std::int64_t)raw * (std::int64_t)other.raw; temp += fp_t::K; - result = (int32_t)(temp >> fp_t::Q); + result = (std::int32_t)(temp >> fp_t::Q); return {result}; } +fp_t fp_t::operator/(const fp_t& other) const { + if (other.raw == 0) { + std::fprintf(stderr, "fp_t: division by zero\n"); + std::abort(); + } + std::int64_t temp = (std::int64_t)raw << fp_t::Q; + + if ((temp >= 0 && other.raw >= 0) || (temp < 0 && other.raw < 0)) { + temp += (other.raw / 2); + } else { + temp -= (other.raw / 2); + } + return {(std::int32_t)(temp / other.raw)}; +} + bool fp_t::operator==(const fp_t& other) const { return raw == other.raw; } diff --git a/tests/test_fixed_point.cpp b/tests/test_fixed_point.cpp index 6d1a594..8c5c269 100644 --- a/tests/test_fixed_point.cpp +++ b/tests/test_fixed_point.cpp @@ -50,3 +50,49 @@ TEST_CASE("fp_t multiplication drops percision under 1 ULP") { fp_t b = fp_t::from_raw(0); CHECK(a * a == b); } + +TEST_CASE("fp_t division is basic") { + fp_t a = fp_t::from_int(10); + fp_t b = fp_t::from_int(2); + fp_t c = fp_t::from_int(5); + CHECK(a / b == c); +} + +TEST_CASE("fp_t division is sign-symmetric") { + fp_t a = fp_t::from_int(1); + fp_t b = fp_t::from_raw(-3); + fp_t neg_a = fp_t::from_raw(-a.raw); + fp_t neg_b = fp_t::from_raw(-b.raw); + CHECK(neg_a / b == a / neg_b); +} + +TEST_CASE("fp_t division rounds to nearest") { + fp_t a = fp_t::from_int(1); + fp_t b = fp_t::from_int(3); + fp_t c = fp_t::from_raw(21845); + CHECK(a / b == c); +} + +TEST_CASE("fp_t division handles negative operands") { + fp_t a = fp_t::from_int(-10); + fp_t b = fp_t::from_int(2); + fp_t c = fp_t::from_int(-5); + CHECK(a / b == c); + + fp_t d = fp_t::from_int(10); + fp_t e = fp_t::from_int(-2); + fp_t f = fp_t::from_int(-5); + CHECK(d / e == f); + + fp_t g = fp_t::from_int(-10); + fp_t h = fp_t::from_int(-2); + fp_t i = fp_t::from_int(5); + CHECK(g / h == i); +} + +TEST_CASE("fp_t division truncates precision below 1 ULP") { + fp_t a = fp_t::from_raw(1); + fp_t b = fp_t::from_int(100000); + fp_t c = fp_t::from_raw(0); + CHECK(a / b == c); +} |
