root/software/fixed/fixed.h @ c7e0510c
| 835f4592 | David Sorber | |||
| 7fa2f28e | David Sorber | #ifndef __FIXED_H_
|
||
#define __FIXED_H_
|
||||
| 835f4592 | David Sorber | |||
| 11ae9ac5 | David Sorber | #include <cctype>
|
||
| 835f4592 | David Sorber | #include <cmath>
|
||
#include <cstdint>
|
||||
| c7e0510c | David Sorber | #include <cstdlib>
|
||
| 11ae9ac5 | David Sorber | #include <iomanip>
|
||
#include <iostream>
|
||||
| 835f4592 | David Sorber | #include <limits>
|
||
| 11ae9ac5 | David Sorber | #include <sstream>
|
||
| c7e0510c | David Sorber | #include <stdexcept>
|
||
| 835f4592 | David Sorber | #include <type_traits>
|
||
| 7fa2f28e | David Sorber | #if __GNUC__ >= 3
|
||
| c7e0510c | David Sorber | #define __unlikely(cond) __builtin_expect((cond), 0)
|
||
#define __likely(cond) __builtin_expect((cond), 1)
|
||||
| 7fa2f28e | David Sorber | #else
|
||
| c7e0510c | David Sorber | #define __unlikely(cond) (cond)
|
||
#define __likely(cond) (cond)
|
||||
| 7fa2f28e | David Sorber | #endif
|
||
| c7e0510c | David Sorber | template<typename IntegerType, typename FractionalType>
|
||
class fixed
|
||||
| 835f4592 | David Sorber | {
|
||
| c7e0510c | David Sorber | static_assert(std::is_integral<IntegerType>::value,
|
||
"IntegerType must be an integral type");
|
||||
static_assert(std::is_integral<FractionalType>::value,
|
||||
"FractionalType must be an integral type");
|
||||
static_assert(std::is_signed<IntegerType>::value,
|
||||
"IntegerType must be a signed type");
|
||||
static_assert(std::is_unsigned<FractionalType>::value,
|
||||
"FractionalType must be an unsigned type");
|
||||
| 835f4592 | David Sorber | public:
|
||
static constexpr size_t integer_bits = sizeof(IntegerType) * 8;
|
||||
static constexpr size_t fractional_bits = sizeof(FractionalType) * 8;
|
||||
| c7e0510c | David Sorber | |||
static constexpr size_t integer_decimal_digits =
|
||||
std::floor(std::log10(std::numeric_limits<IntegerType>::max()));
|
||||
static constexpr size_t fractional_decimal_digits =
|
||||
std::floor(std::log10(std::numeric_limits<FractionalType>::max()));
|
||||
static constexpr size_t MAX_INTEGER_VALUE =
|
||||
(static_cast<uint64_t>(std::pow(10, integer_decimal_digits)) - 1);
|
||||
| 835f4592 | David Sorber | static constexpr size_t MIN_INTEGER_VALUE = 0;
|
||
| c7e0510c | David Sorber | |||
static constexpr size_t MAX_FRACTIONAL_VALUE =
|
||||
(static_cast<uint64_t>(std::pow(10, fractional_decimal_digits)) - 1);
|
||||
| 835f4592 | David Sorber | static constexpr size_t MIN_FRACTIONAL_VALUE = 0;
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | static const uint64_t SCALE_VALUES[20];
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | // Constructors
|
||
| 835f4592 | David Sorber | fixed()
|
||
| c7e0510c | David Sorber | : m_integer(0),
|
||
m_fractional(0)
|
||||
| 11ae9ac5 | David Sorber | {}
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | fixed(IntegerType integerVal)
|
||
| c7e0510c | David Sorber | : m_integer(__checkIntOverflow(integerVal)),
|
||
m_fractional(0)
|
||||
| 11ae9ac5 | David Sorber | {}
|
||
| c7e0510c | David Sorber | |||
| 835f4592 | David Sorber | fixed(IntegerType integerVal, FractionalType fractionalVal)
|
||
| c7e0510c | David Sorber | : m_integer(__checkIntOverflow(integerVal)),
|
||
m_fractional(__checkFracOverflow(fractionalVal))
|
||||
| 11ae9ac5 | David Sorber | {
|
||
// Scale the fractional value appropriately
|
||||
uint32_t idx = 0;
|
||||
for (; idx < fractional_decimal_digits; ++idx)
|
||||
{
|
||||
if (SCALE_VALUES[idx] > m_fractional)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
m_fractional = __checkFracOverflow(
|
||||
m_fractional * SCALE_VALUES[fractional_decimal_digits - idx]);
|
||||
| 11ae9ac5 | David Sorber | }
|
||
| c7e0510c | David Sorber | |||
| 835f4592 | David Sorber | // Default copy constructor, and assignment operator
|
||
| c7e0510c | David Sorber | fixed(const fixed&) = default;
|
||
fixed& operator=(const fixed&) = default;
|
||||
| 11ae9ac5 | David Sorber | // Reassign value to type
|
||
void assign(IntegerType integerVal, FractionalType fractionalVal)
|
||||
{
|
||||
m_integer = __checkIntOverflow(integerVal);
|
||||
// Scale the fractional value appropriately
|
||||
uint32_t idx = 0;
|
||||
for (; idx < fractional_decimal_digits; ++idx)
|
||||
{
|
||||
if (SCALE_VALUES[idx] > fractionalVal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
m_fractional = __checkFracOverflow(
|
||||
fractionalVal * SCALE_VALUES[fractional_decimal_digits - idx]);
|
||||
| 11ae9ac5 | David Sorber | }
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | // Parse value from string input
|
||
| 7fa2f28e | David Sorber | uint32_t parse(const char* input)
|
||
| 835f4592 | David Sorber | {
|
||
| 11ae9ac5 | David Sorber | char* endPtr = nullptr;
|
||
| c7e0510c | David Sorber | m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
|
||
| 11ae9ac5 | David Sorber | m_fractional = 0;
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | if (std::isdigit(*endPtr))
|
||
{
|
||||
throw std::out_of_range("Integer value is out of range.");
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | // If the ending char is a period we can now parse the fractional part
|
||
if (*endPtr == '.')
|
||||
{
|
||||
char* fracEndPtr = nullptr;
|
||||
| c7e0510c | David Sorber | FractionalType fracTemp =
|
||
__checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
|
||||
| 11ae9ac5 | David Sorber | uint32_t fracLen = (fracEndPtr - endPtr) - 1;
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | fracLen = fractional_decimal_digits - fracLen;
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | m_fractional = __checkFracOverflow(fracTemp * SCALE_VALUES[fracLen]);
|
||
| 7fa2f28e | David Sorber | endPtr = fracEndPtr;
|
||
| 11ae9ac5 | David Sorber | }
|
||
| c7e0510c | David Sorber | |||
// Calculate and return overall length
|
||||
| 7fa2f28e | David Sorber | return (endPtr - input);
|
||
| 835f4592 | David Sorber | }
|
||
| c7e0510c | David Sorber | |||
| 835f4592 | David Sorber | constexpr inline fixed operator-() const noexcept = delete;
|
||
| 11ae9ac5 | David Sorber | constexpr inline fixed operator!() const noexcept = delete;
|
||
constexpr inline fixed operator~() const noexcept = delete;
|
||||
| 835f4592 | David Sorber | inline fixed& operator+=(const fixed& y) noexcept = delete;
|
||
inline fixed& operator-=(const fixed& y) noexcept = delete;
|
||||
inline fixed& operator*=(const fixed& y) noexcept = delete;
|
||||
| c7e0510c | David Sorber | inline fixed& operator/=(const fixed& y) noexcept = delete;
|
||
| 835f4592 | David Sorber | // Comparison operators
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
friend constexpr inline bool operator==(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||||
template<typename I, typename F>
|
||||
friend constexpr inline bool operator!=(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||||
template<typename I, typename F>
|
||||
friend constexpr inline bool operator<(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||||
template<typename I, typename F>
|
||||
friend constexpr inline bool operator>(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||||
template<typename I, typename F>
|
||||
friend constexpr inline bool operator<=(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||||
template<typename I, typename F>
|
||||
friend constexpr inline bool operator>=(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||||
| 11ae9ac5 | David Sorber | // Output stream operator
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
friend inline std::ostream& operator<<(
|
||||
std::ostream& os, const fixed<I, F>& rhs);
|
||||
| 835f4592 | David Sorber | private:
|
||
| 11ae9ac5 | David Sorber | static inline IntegerType __checkIntOverflow(IntegerType integerVal)
|
||
{
|
||||
| 7fa2f28e | David Sorber | if (__unlikely(integerVal > MAX_INTEGER_VALUE))
|
||
| 11ae9ac5 | David Sorber | {
|
||
std::ostringstream msg;
|
||||
msg << "Integer value: " << integerVal << " exceeds maximum "
|
||||
| c7e0510c | David Sorber | << "integer range of type (" << MAX_INTEGER_VALUE << ")!";
|
||
| 11ae9ac5 | David Sorber | throw std::out_of_range(msg.str());
|
||
}
|
||||
return integerVal;
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | static inline FractionalType __checkFracOverflow(FractionalType fractionalVal)
|
||
{
|
||||
| 7fa2f28e | David Sorber | if (__unlikely(fractionalVal > MAX_FRACTIONAL_VALUE))
|
||
| 11ae9ac5 | David Sorber | {
|
||
std::ostringstream msg;
|
||||
msg << "Fractional value: " << fractionalVal << " exceeds maximum "
|
||||
| c7e0510c | David Sorber | << "fractional range of type (" << MAX_FRACTIONAL_VALUE << ")!";
|
||
| 11ae9ac5 | David Sorber | throw std::out_of_range(msg.str());
|
||
}
|
||||
return fractionalVal;
|
||||
}
|
||||
| 835f4592 | David Sorber | IntegerType m_integer;
|
||
FractionalType m_fractional;
|
||||
};
|
||||
| 11ae9ac5 | David Sorber | // Predefined types
|
||
using fixed_8_8 = fixed<int8_t, uint8_t>;
|
||||
using fixed_8_16 = fixed<int8_t, uint16_t>;
|
||||
using fixed_8_32 = fixed<int8_t, uint32_t>;
|
||||
using fixed_8_64 = fixed<int8_t, uint64_t>;
|
||||
using fixed_16_8 = fixed<int16_t, uint8_t>;
|
||||
using fixed_16_16 = fixed<int16_t, uint16_t>;
|
||||
using fixed_16_32 = fixed<int16_t, uint32_t>;
|
||||
using fixed_16_64 = fixed<int16_t, uint64_t>;
|
||||
using fixed_32_8 = fixed<int32_t, uint8_t>;
|
||||
using fixed_32_16 = fixed<int32_t, uint16_t>;
|
||||
using fixed_32_32 = fixed<int32_t, uint32_t>;
|
||||
using fixed_32_64 = fixed<int32_t, uint64_t>;
|
||||
using fixed_64_8 = fixed<int64_t, uint8_t>;
|
||||
using fixed_64_16 = fixed<int64_t, uint16_t>;
|
||||
using fixed_64_32 = fixed<int64_t, uint32_t>;
|
||||
| 835f4592 | David Sorber | using fixed_64_64 = fixed<int64_t, uint64_t>;
|
||
| 11ae9ac5 | David Sorber | // Precomputed scale value constants
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
const uint64_t fixed<I, F>::SCALE_VALUES[20] =
|
||||
| 11ae9ac5 | David Sorber | {
|
||
| c7e0510c | David Sorber | /* 0 */ 1ULL,
|
||
/* 1 */ 10ULL,
|
||||
/* 2 */ 100ULL,
|
||||
| 11ae9ac5 | David Sorber | /* 3 */ 1000ULL,
|
||
/* 4 */ 10000ULL,
|
||||
/* 5 */ 100000ULL,
|
||||
/* 6 */ 1000000ULL,
|
||||
/* 7 */ 10000000ULL,
|
||||
/* 8 */ 100000000ULL,
|
||||
/* 9 */ 1000000000ULL,
|
||||
/* 10 */ 10000000000ULL,
|
||||
/* 11 */ 100000000000ULL,
|
||||
/* 12 */ 1000000000000ULL,
|
||||
/* 13 */ 10000000000000ULL,
|
||||
/* 14 */ 100000000000000ULL,
|
||||
/* 15 */ 1000000000000000ULL,
|
||||
/* 16 */ 10000000000000000ULL,
|
||||
/* 17 */ 100000000000000000ULL,
|
||||
/* 18 */ 1000000000000000000ULL,
|
||||
/* 19 */ 10000000000000000000ULL
|
||||
};
|
||||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline bool operator==(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||||
| 835f4592 | David Sorber | {
|
||
return (x.m_integer == y.m_integer && x.m_fractional == y.m_fractional);
|
||||
}
|
||||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline bool operator!=(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||||
| 835f4592 | David Sorber | {
|
||
| 11ae9ac5 | David Sorber | return (! (x == y));
|
||
| 835f4592 | David Sorber | }
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline bool operator<(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||||
| 835f4592 | David Sorber | {
|
||
if (x.m_integer == y.m_integer)
|
||||
{
|
||||
return (x.m_fractional < y.m_fractional);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (x.m_integer < y.m_integer);
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
return false;
|
||||
| 835f4592 | David Sorber | }
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline bool operator>(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||||
| 835f4592 | David Sorber | {
|
||
if (x.m_integer == y.m_integer)
|
||||
{
|
||||
return (x.m_fractional > y.m_fractional);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (x.m_integer > y.m_integer);
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
return false;
|
||||
| 835f4592 | David Sorber | }
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline bool operator<=(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||||
| 835f4592 | David Sorber | {
|
||
if (x.m_integer == y.m_integer)
|
||||
{
|
||||
return (x.m_fractional <= y.m_fractional);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (x.m_integer < y.m_integer);
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
return false;
|
||||
| 835f4592 | David Sorber | }
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline bool operator>=(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||||
| 835f4592 | David Sorber | {
|
||
if (x.m_integer == y.m_integer)
|
||||
{
|
||||
| 11ae9ac5 | David Sorber | return (x.m_fractional >= y.m_fractional);
|
||
| 835f4592 | David Sorber | }
|
||
else
|
||||
{
|
||||
return (x.m_integer > y.m_integer);
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
| 835f4592 | David Sorber | return false;
|
||
}
|
||||
| 11ae9ac5 | David Sorber | // Addition -- not yet supported
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline fixed<I, F> operator+(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 11ae9ac5 | David Sorber | // Subtraction -- not yet supported
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline fixed<I, F> operator-(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
// Multiplication
|
||||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline fixed<I, F> operator*(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 11ae9ac5 | David Sorber | // Division -- not yet supported
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
constexpr inline fixed<I, F> operator/(
|
||||
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 11ae9ac5 | David Sorber | // Stream output
|
||
| c7e0510c | David Sorber | template<typename I, typename F>
|
||
| 11ae9ac5 | David Sorber | inline std::ostream& operator<<(std::ostream& os, const fixed<I, F>& rhs)
|
||
{
|
||||
| c7e0510c | David Sorber | return os << std::fixed << rhs.m_integer << "."
|
||
<< std::setw(fixed<I, F>::fractional_decimal_digits)
|
||||
<< std::setfill('0') << rhs.m_fractional;
|
||||
| 11ae9ac5 | David Sorber | }
|
||
| c7e0510c | David Sorber | #endif // FIXED_H_
|