aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2024-02-19 21:36:19 +0100
committer hop311 <hop3114@gmail.com>2024-02-19 21:36:19 +0100
commitf5eb9a282891dfee0cfedd5533615448fed2528c (patch)
treee2eb504e7bb2fc367dbab67d9f7ae153db8647d6
parent35e5f7828a41736194362186ad4f946fad5964d1 (diff)
Fixed point rounding functions and sources cleanup
-rw-r--r--src/openvic-simulation/types/fixed_point/FixedPoint.hpp73
-rw-r--r--src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp25
-rw-r--r--src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp4
-rw-r--r--src/openvic-simulation/types/fixed_point/FixedPointMath.hpp11
-rw-r--r--src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py4
-rw-r--r--src/openvic-simulation/utility/Getters.hpp2
6 files changed, 62 insertions, 57 deletions
diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp
index 6a47194..95d2759 100644
--- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp
+++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp
@@ -6,7 +6,7 @@
#include <sstream>
#include <string_view>
-#include "openvic-simulation/types/fixed_point/FixedPointLUT.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
#include "openvic-simulation/utility/Logger.hpp"
#include "openvic-simulation/utility/NumberUtils.hpp"
#include "openvic-simulation/utility/StringUtils.hpp"
@@ -18,14 +18,32 @@ namespace OpenVic {
static constexpr size_t SIZE = 8;
- static constexpr int32_t PRECISION = FPLUT::SIN_LUT_PRECISION;
+ static constexpr int32_t PRECISION = 16;
static constexpr int64_t ONE = 1 << PRECISION;
+ static constexpr int64_t FRAC_MASK = ONE - 1;
+ /* Fixed points represent any base 2 number with 48 bits above the point and 16 bits below it.
+ * - Any number expressible as n / 2^16 where n is an int64_t can be converted to a fixed point exactly.
+ * - If a number cannot be expressed as an integer divided by 2^16, it will be rounded down to the first
+ * representable number below it. For example: 0.01 = 655.36 / 2^16 becomes 0.0099945068359375 = 655 / 2^16.
+ * - While numbers with an integer part greater than 2^32 can be represented, they do not work properly with
+ * multiplication and division which involve an intermediate value that is 2^16 times larger than the result.
+ * For numbers with integer part smaller than 2^32 the top 16 bits prevent this intermediate value from
+ * overflowing. For larger values this will not work and the result will be missing its most significant bits. */
+
+ private:
+ int64_t PROPERTY_RW_CUSTOM_NAME(value, get_raw_value, set_raw_value);
+
+ /* Sin lookup table */
+ #include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp"
+
+ static_assert(SIN_LUT_PRECISION == PRECISION);
+
+ public:
constexpr fixed_point_t() : value { 0 } {}
constexpr fixed_point_t(int64_t new_value) : value { new_value } {}
constexpr fixed_point_t(int32_t new_value) : value { static_cast<int64_t>(new_value) << PRECISION } {}
- // Trivial destructor
constexpr ~fixed_point_t() = default;
static constexpr fixed_point_t max() {
@@ -200,6 +218,23 @@ namespace OpenVic {
return static_cast<int64_t>(178145LL);
}
+ constexpr fixed_point_t sin() const {
+ int64_t num = (*this % pi2() * one_div_pi2()).get_raw_value();
+
+ const bool negative = num < 0;
+ if (negative) {
+ num = -num;
+ }
+
+ const int64_t index = num >> SIN_LUT_SHIFT;
+ const int64_t a = SIN_LUT[index];
+ const int64_t b = SIN_LUT[index + 1];
+
+ const int64_t fraction = (num - (index << SIN_LUT_SHIFT)) << SIN_LUT_COUNT_LOG2;
+ const int64_t result = a + (((b - a) * fraction) >> SIN_LUT_PRECISION);
+ return !negative ? result : -result;
+ }
+
constexpr bool is_negative() const {
return value < 0;
}
@@ -216,23 +251,35 @@ namespace OpenVic {
// Doesn't account for sign, so -n.abc -> 1 - 0.abc
constexpr fixed_point_t get_frac() const {
- return value & (ONE - 1);
+ return value & FRAC_MASK;
}
constexpr bool is_integer() const {
return get_frac() == 0;
}
- constexpr int64_t to_int64_t() const {
- return value >> PRECISION;
+ constexpr fixed_point_t floor() const {
+ return value & ~FRAC_MASK;
}
- constexpr void set_raw_value(int64_t value) {
- this->value = value;
+ constexpr fixed_point_t ceil() const {
+ return floor() + !is_integer();
}
- constexpr int64_t get_raw_value() const {
- return value;
+ /* WARNING: the results of these rounding functions are affected by the accuracy of base 2 fixed point numbers,
+ * for example 1.0 rounded to a multiple of 0.01 is 0.99945068359375 for down and 1.0094451904296875 for up. */
+ constexpr fixed_point_t round_down_to_multiple(fixed_point_t factor) const {
+ const fixed_point_t remainder = *this % factor;
+ return *this - remainder - (remainder < 0 ? factor : _0());
+ }
+
+ constexpr fixed_point_t round_up_to_multiple(fixed_point_t factor) const {
+ const fixed_point_t remainder = *this % factor;
+ return *this - remainder + (remainder > 0 ? factor : _0());
+ }
+
+ constexpr int64_t to_int64_t() const {
+ return value >> PRECISION;
}
constexpr int32_t to_int32_t() const {
@@ -251,13 +298,13 @@ namespace OpenVic {
return value / static_cast<double>(ONE);
}
- constexpr float to_double_rounded() const {
+ constexpr double to_double_rounded() const {
return NumberUtils::round_to_int64((value / static_cast<double>(ONE)) * 100000.0) / 100000.0;
}
static std::ostream& print(std::ostream& stream, fixed_point_t val, size_t decimal_places = 0) {
if (decimal_places > 0) {
- fixed_point_t err = fixed_point_t::_0_50();
+ fixed_point_t err = _0_50();
for (size_t i = decimal_places; i > 0; --i) {
err /= 10;
}
@@ -612,8 +659,6 @@ namespace OpenVic {
}
private:
- int64_t value;
-
static constexpr fixed_point_t parse_integer(char const* str, char const* const end, bool* successful) {
int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10);
return parse(parsed_value);
diff --git a/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp b/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp
deleted file mode 100644
index a5d585f..0000000
--- a/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace OpenVic::FPLUT {
-
-#include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp"
-
- constexpr int32_t SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2;
-
- constexpr int64_t sin(int64_t value) {
- int sign = 1;
- if (value < 0) {
- value = -value;
- sign = -1;
- }
-
- int index = value >> SHIFT;
- int64_t a = SIN_LUT[index];
- int64_t b = SIN_LUT[index + 1];
- int64_t fraction = (value - (index << SHIFT)) << SIN_LUT_COUNT_LOG2;
- int64_t result = a + (((b - a) * fraction) >> SIN_LUT_PRECISION);
- return result * sign;
- }
-}
diff --git a/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp b/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp
index 0c75efe..2b935a3 100644
--- a/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp
+++ b/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp
@@ -1,9 +1,7 @@
-#pragma once
-
-#include <cstdint>
static constexpr int32_t SIN_LUT_PRECISION = 16;
static constexpr int32_t SIN_LUT_COUNT_LOG2 = 9;
+static constexpr int32_t SIN_LUT_SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2;
static constexpr int64_t SIN_LUT[(1 << SIN_LUT_COUNT_LOG2) + 1] = {
0, 804, 1608, 2412, 3216, 4019, 4821, 5623, 6424, 7224, 8022, 8820, 9616, 10411, 11204, 11996,
diff --git a/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp b/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp
deleted file mode 100644
index 6cdb926..0000000
--- a/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
-
-namespace OpenVic::FPMath {
- constexpr fixed_point_t sin(fixed_point_t number) {
- number %= fixed_point_t::pi2();
- number *= fixed_point_t::one_div_pi2();
- return FPLUT::sin(number.get_raw_value());
- }
-}
diff --git a/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py b/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py
index 8ae7a32..e20eaa3 100644
--- a/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py
+++ b/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py
@@ -20,12 +20,10 @@ def generate_sin_lut(precision : int, count_log2 : int):
SinLut.append(SinLut[0])
output = [
- "#pragma once",
- "",
- "#include <cstdint>",
"",
f"static constexpr int32_t SIN_LUT_PRECISION = {precision};",
f"static constexpr int32_t SIN_LUT_COUNT_LOG2 = {count_log2};",
+ "static constexpr int32_t SIN_LUT_SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2;",
"",
"static constexpr int64_t SIN_LUT[(1 << SIN_LUT_COUNT_LOG2) + 1] = {"
]
diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp
index fa76e74..0a6917c 100644
--- a/src/openvic-simulation/utility/Getters.hpp
+++ b/src/openvic-simulation/utility/Getters.hpp
@@ -121,7 +121,7 @@ ACCESS:
#define PROPERTY_RW_FULL(NAME, GETTER_NAME, SETTER_NAME, ACCESS) \
PROPERTY_FULL(NAME, GETTER_NAME, ACCESS) \
public: \
- void SETTER_NAME(decltype(NAME) new_##NAME) { \
+ constexpr void SETTER_NAME(decltype(NAME) new_##NAME) { \
NAME = new_##NAME; \
} \
ACCESS: