root/software/fixed/fixed.h @ 65b83771
| 835f4592 | David Sorber | |||
| 7fa2f28e | David Sorber | #ifndef __FIXED_H_
|
||
#define __FIXED_H_
|
||||
| 835f4592 | David Sorber | |||
| 92782e4a | David Sorber | #define __STDC_FORMAT_MACROS
|
||
| 11ae9ac5 | David Sorber | #include <cctype>
|
||
| 835f4592 | David Sorber | #include <cmath>
|
||
| 92782e4a | David Sorber | #include <cinttypes>
|
||
#include <cstdio>
|
||||
| 835f4592 | David Sorber | #include <cstdint>
|
||
| c7e0510c | David Sorber | #include <cstdlib>
|
||
| 495f615f | David Sorber | #include <cstring>
|
||
| 11ae9ac5 | David Sorber | #include <iomanip>
|
||
#include <iostream>
|
||||
| 835f4592 | David Sorber | #include <limits>
|
||
| 495f615f | David Sorber | #include <map>
|
||
| 11ae9ac5 | David Sorber | #include <sstream>
|
||
| c7e0510c | David Sorber | #include <stdexcept>
|
||
| 835f4592 | David Sorber | #include <type_traits>
|
||
| 495f615f | David Sorber | |||
#ifndef CYBERLYNX
|
||||
#include <boost/serialization/access.hpp>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#endif
|
||||
| 835f4592 | David Sorber | |||
| 852d0468 | David Sorber | |||
| 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
|
||
| b9f341b6 | David Sorber | // Defaults for compile time options
|
||
#ifndef BEHAVIOR_PARSE_TRUNCATE
|
||||
#define BEHAVIOR_PARSE_TRUNCATE 0 // Default: disable
|
||||
#endif
|
||||
#ifndef SUPPORT_PARSE_EXPONENT
|
||||
#define SUPPORT_PARSE_EXPONENT 1 // Default: enable
|
||||
#endif
|
||||
| 495f615f | David Sorber | #ifndef SUPPORT_128_INTS
|
||
#define SUPPORT_128_INTS 1 // Default: enable
|
||||
#endif
|
||||
#if SUPPORT_128_INTS
|
||||
| 30849ea7 | David Sorber | #include "int128.h"
|
||
//~ #include "Utilities/int128.h"
|
||||
| 495f615f | David Sorber | #endif
|
||
| 852d0468 | David Sorber | template<typename T>
|
||
constexpr T calculateMax(size_t decimal_digits)
|
||||
{
|
||||
T val = 0;
|
||||
for (uint32_t idx = 0; idx < decimal_digits; ++idx)
|
||||
{
|
||||
val *= 10;
|
||||
val += 9;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
template<typename IntegerType, typename FractionalType,
|
||||
typename StrToIntegerTypeFunc,
|
||||
typename StrToFracTypeFunc>
|
||||
| c7e0510c | David Sorber | 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()));
|
||||
| b9f341b6 | David Sorber | static constexpr size_t total_decimal_digits = integer_decimal_digits +
|
||
fractional_decimal_digits;
|
||||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | static constexpr IntegerType MAX_INTEGER_VALUE =
|
||
(calculateMax<IntegerType>(integer_decimal_digits));
|
||||
| 495f615f | David Sorber | static constexpr IntegerType MIN_INTEGER_VALUE = -MAX_INTEGER_VALUE;
|
||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | static constexpr FractionalType MAX_FRACTIONAL_VALUE =
|
||
(calculateMax<FractionalType>(fractional_decimal_digits));
|
||||
static constexpr FractionalType MIN_FRACTIONAL_VALUE = 0;
|
||||
| c7e0510c | David Sorber | |||
| b9f341b6 | David Sorber | static constexpr IntegerType NEGATIVE_ZERO = MAX_INTEGER_VALUE + 2;
|
||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
static const uint128_t SCALE_VALUES[39];
|
||||
static const std::map<uint128_t, uint32_t> DIGIT_LOOKUP_TABLE;
|
||||
#else
|
||||
| 11ae9ac5 | David Sorber | static const uint64_t SCALE_VALUES[20];
|
||
| 92782e4a | David Sorber | static const std::map<uint64_t, uint32_t> DIGIT_LOOKUP_TABLE;
|
||
| 495f615f | David Sorber | #endif
|
||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | // Constructors
|
||
| b9f341b6 | David Sorber | explicit fixed()
|
||
| c7e0510c | David Sorber | : m_integer(0),
|
||
m_fractional(0)
|
||||
| 11ae9ac5 | David Sorber | {}
|
||
| c7e0510c | David Sorber | |||
| b9f341b6 | David Sorber | explicit fixed(IntegerType integerVal)
|
||
| c7e0510c | David Sorber | : m_integer(__checkIntOverflow(integerVal)),
|
||
m_fractional(0)
|
||||
| 11ae9ac5 | David Sorber | {}
|
||
| c7e0510c | David Sorber | |||
| b9f341b6 | David Sorber | explicit fixed(IntegerType integerVal, FractionalType fractionalVal)
|
||
| c7e0510c | David Sorber | : m_integer(__checkIntOverflow(integerVal)),
|
||
m_fractional(__checkFracOverflow(fractionalVal))
|
||||
| 11ae9ac5 | David Sorber | {
|
||
// Scale the fractional value appropriately
|
||||
| 92782e4a | David Sorber | m_fractional = __checkFracOverflow(m_fractional *
|
||
getFracScaleValue(m_fractional));
|
||||
}
|
||||
| b9f341b6 | David Sorber | explicit fixed(IntegerType integerVal, FractionalType fractionalVal, uint32_t leadingZeros)
|
||
| 92782e4a | David Sorber | : m_integer(__checkIntOverflow(integerVal)),
|
||
m_fractional(__checkFracOverflow(fractionalVal))
|
||||
{
|
||||
// Scale the fractional value appropriately
|
||||
m_fractional = __checkFracOverflow(m_fractional *
|
||||
getFracScaleValue(m_fractional,
|
||||
leadingZeros));
|
||||
| 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;
|
||||
| 92782e4a | David Sorber | // Reassign value to type with a prescaled fractional value.
|
||
| 11ae9ac5 | David Sorber | void assign(IntegerType integerVal, FractionalType fractionalVal)
|
||
{
|
||||
m_integer = __checkIntOverflow(integerVal);
|
||||
| 92782e4a | David Sorber | |||
// Fractional value is prescaled to fractional type precision
|
||||
m_fractional = __checkFracOverflow(fractionalVal);
|
||||
}
|
||||
| 495f615f | David Sorber | |||
// Reassign value from existing fixed value of the same type
|
||||
void assign(const fixed<IntegerType,
|
||||
FractionalType,
|
||||
StrToIntegerTypeFunc,
|
||||
StrToFracTypeFunc>& other)
|
||||
{
|
||||
m_integer = other.m_integer;
|
||||
m_fractional = other.m_fractional;
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
| 92782e4a | David Sorber | // Assign a new value with an *unscaled* fractional part (NOTE: use the
|
||
// "leadingZeros" parameter to represent the number of leading zeros in the
|
||||
// unscaled fractional part.
|
||||
void assignUnscaledFrac(
|
||||
IntegerType integerVal,
|
||||
FractionalType fractionalVal,
|
||||
uint32_t leadingZeros=0)
|
||||
{
|
||||
m_integer = __checkIntOverflow(integerVal);
|
||||
// Scale the fractional value appropriately
|
||||
m_fractional = __checkFracOverflow(fractionalVal *
|
||||
getFracScaleValue(fractionalVal,
|
||||
leadingZeros));
|
||||
| 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;
|
||
| 852d0468 | David Sorber | m_integer = __checkIntOverflow(strto_inttype(input, &endPtr, 10));
|
||
| 11ae9ac5 | David Sorber | m_fractional = 0;
|
||
| b9f341b6 | David Sorber | bool negativeZeroFlag = false;
|
||
| 495f615f | David Sorber | |||
| 11ae9ac5 | David Sorber | if (std::isdigit(*endPtr))
|
||
{
|
||||
throw std::out_of_range("Integer value is out of range.");
|
||||
}
|
||||
| b9f341b6 | David Sorber | |||
// Check for negative zero corner case with leading zero digit
|
||||
| 495f615f | David Sorber | if (__unlikely(m_integer == 0 && ((endPtr - input) >= 2) &&
|
||
*(endPtr - 2) == '-'))
|
||||
| b9f341b6 | David Sorber | {
|
||
negativeZeroFlag = true;
|
||||
}
|
||||
| c7e0510c | David Sorber | |||
| 11ae9ac5 | David Sorber | // If the ending char is a period we can now parse the fractional part
|
||
| b9f341b6 | David Sorber | uint32_t fracLen = 0;
|
||
| 495f615f | David Sorber | if (*endPtr == '.'|| ((endPtr[0] == '-' || endPtr[0] == '+') && endPtr[1] == '.'))
|
||
| 11ae9ac5 | David Sorber | {
|
||
| b9f341b6 | David Sorber | // Check for negative zero corner case without leading zero digit
|
||
| 495f615f | David Sorber | if (__unlikely(endPtr[0] == '-'|| endPtr[0] == '+'))
|
||
| b9f341b6 | David Sorber | {
|
||
| 495f615f | David Sorber | negativeZeroFlag = (endPtr[0] == '-');
|
||
| b9f341b6 | David Sorber | ++endPtr;
|
||
}
|
||||
| 11ae9ac5 | David Sorber | char* fracEndPtr = nullptr;
|
||
| c7e0510c | David Sorber | FractionalType fracTemp =
|
||
| 852d0468 | David Sorber | __checkFracOverflow(strto_fractype(endPtr + 1, &fracEndPtr, 10));
|
||
| 92782e4a | David Sorber | |||
| b9f341b6 | David Sorber | fracLen = (fracEndPtr - endPtr) - 1;
|
||
if (__unlikely(fracLen > fractional_decimal_digits))
|
||||
{
|
||||
#if BEHAVIOR_PARSE_TRUNCATE
|
||||
// Fractional length exceeds supported number of fractional
|
||||
// decimal digits, downscale value to only include supported
|
||||
// number of digits
|
||||
uint32_t idx = fracLen - fractional_decimal_digits;
|
||||
m_fractional = fracTemp / SCALE_VALUES[idx];
|
||||
#else
|
||||
throw std::out_of_range("Fractional length exceeds supported "
|
||||
"number of fractional decimal digits.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t idx = fractional_decimal_digits - fracLen;
|
||||
m_fractional = __checkFracOverflow(fracTemp * SCALE_VALUES[idx]);
|
||||
}
|
||||
| 7fa2f28e | David Sorber | endPtr = fracEndPtr;
|
||
| 11ae9ac5 | David Sorber | }
|
||
| c7e0510c | David Sorber | |||
| b9f341b6 | David Sorber | #if SUPPORT_PARSE_EXPONENT
|
||
// Attempt to parse exponent
|
||||
if (*endPtr == 'e' || *endPtr == 'E')
|
||||
{
|
||||
// Set negative zero flag here if integer part is negative then
|
||||
// clear below if final integer part after exponent adjustment is
|
||||
// not zero
|
||||
if (m_integer < 0)
|
||||
{
|
||||
negativeZeroFlag = true;
|
||||
}
|
||||
char* expEndPtr = nullptr;
|
||||
long exponent = strtol(++endPtr, &expEndPtr, 10);
|
||||
uint32_t integerLen = 0;
|
||||
if (m_integer != 0)
|
||||
{
|
||||
integerLen = DIGIT_LOOKUP_TABLE.upper_bound(std::abs(m_integer))->second;
|
||||
}
|
||||
uint32_t scaledFracLen =
|
||||
DIGIT_LOOKUP_TABLE.upper_bound(m_fractional)->second;
|
||||
if (exponent >= 0)
|
||||
{
|
||||
// Detect potential overflow based on the exponent
|
||||
if ((m_integer != 0) &&
|
||||
(integerLen + static_cast<size_t>(exponent)) >
|
||||
integer_decimal_digits)
|
||||
{
|
||||
throw std::out_of_range("Positive exponent exceeds maximum"
|
||||
" decimal digit range.");
|
||||
}
|
||||
else if (scaledFracLen + static_cast<size_t>(exponent)
|
||||
> total_decimal_digits)
|
||||
{
|
||||
throw std::out_of_range("Positive exponent exceeds maximum"
|
||||
" decimal digit range (2).");
|
||||
}
|
||||
long fracExponent = exponent;
|
||||
if (static_cast<size_t>(fracExponent) > fractional_decimal_digits)
|
||||
{
|
||||
fracExponent = fractional_decimal_digits;
|
||||
}
|
||||
bool intZeroFlag = true;
|
||||
if (m_integer != 0)
|
||||
{
|
||||
// Rescale integer value if it is greater than zero
|
||||
intZeroFlag = false;
|
||||
m_integer *= static_cast<IntegerType>(SCALE_VALUES[exponent]);
|
||||
}
|
||||
| 495f615f | David Sorber | FractionalType scaler = SCALE_VALUES[fractional_decimal_digits -
|
||
fracExponent];
|
||||
| b9f341b6 | David Sorber | IntegerType temp = m_fractional / scaler;
|
||
| 495f615f | David Sorber | if (m_integer < 0)
|
||
{
|
||||
m_integer += -temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_integer += temp;
|
||||
}
|
||||
| b9f341b6 | David Sorber | m_fractional -= (temp * scaler);
|
||
m_fractional *= SCALE_VALUES[fracExponent];
|
||||
if (intZeroFlag)
|
||||
{
|
||||
// Rescale integer value if it was zero
|
||||
m_integer *= SCALE_VALUES[exponent - fracExponent];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle negative exponent
|
||||
// Detect potential overflow based on the exponent
|
||||
if (m_fractional != 0 &&
|
||||
(static_cast<size_t>(-exponent) >
|
||||
(fractional_decimal_digits - fracLen)))
|
||||
{
|
||||
throw std::out_of_range("Negative exponent exceeds maximum"
|
||||
" decimal digit range.");
|
||||
}
|
||||
else if (static_cast<size_t>(-exponent) >=
|
||||
(integerLen + fractional_decimal_digits))
|
||||
{
|
||||
throw std::out_of_range("Negative exponent exceeds maximum"
|
||||
" decimal digit range.");
|
||||
}
|
||||
FractionalType temp = 0;
|
||||
if (m_fractional != 0)
|
||||
{
|
||||
// CASE 1: fractional non-zero
|
||||
m_fractional /= SCALE_VALUES[-exponent];
|
||||
temp = std::abs(m_integer) % SCALE_VALUES[-exponent];
|
||||
temp *= SCALE_VALUES[fractional_decimal_digits + exponent];
|
||||
m_integer /= static_cast<IntegerType>(SCALE_VALUES[-exponent]);
|
||||
m_fractional += temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// CASE 2: fractional zero
|
||||
| 495f615f | David Sorber | size_t intExponent = -exponent;
|
||
if (intExponent > integer_decimal_digits)
|
||||
| b9f341b6 | David Sorber | {
|
||
intExponent = integer_decimal_digits;
|
||||
}
|
||||
temp = std::abs(m_integer) % SCALE_VALUES[intExponent];
|
||||
temp *= SCALE_VALUES[fractional_decimal_digits - intExponent];
|
||||
| 495f615f | David Sorber | |||
#if SUPPORT_128_INTS
|
||||
uint128_t unsignedAbsIntVal = static_cast<uint128_t>(std::abs(m_integer));
|
||||
#else
|
||||
uint64_t unsignedAbsIntVal = static_cast<uint64_t>(std::abs(m_integer));
|
||||
#endif
|
||||
if (unsignedAbsIntVal < SCALE_VALUES[intExponent])
|
||||
{
|
||||
m_integer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_integer = unsignedAbsIntVal / SCALE_VALUES[intExponent];
|
||||
}
|
||||
| b9f341b6 | David Sorber | m_fractional += temp;
|
||
| 495f615f | David Sorber | if (static_cast<size_t>(std::abs(exponent)) > intExponent)
|
||
| b9f341b6 | David Sorber | {
|
||
long fracExponent = std::abs(exponent) - intExponent;
|
||||
m_fractional /= SCALE_VALUES[fracExponent];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear negative zero flag if final adjusted integer part is not
|
||||
// zero. Flag will remain set iff initial integer part was negative
|
||||
// and integer part after exponent adjustment is zero.
|
||||
if (__likely(m_integer != 0))
|
||||
{
|
||||
negativeZeroFlag = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (__unlikely(negativeZeroFlag))
|
||||
{
|
||||
m_integer = NEGATIVE_ZERO;
|
||||
}
|
||||
| 495f615f | David Sorber | |||
| c7e0510c | David Sorber | // Calculate and return overall length
|
||
| 7fa2f28e | David Sorber | return (endPtr - input);
|
||
| 835f4592 | David Sorber | }
|
||
| c7e0510c | David Sorber | |||
| b9f341b6 | David Sorber | constexpr inline void absval()
|
||
| 92782e4a | David Sorber | {
|
||
| b9f341b6 | David Sorber | if (__unlikely(m_integer == NEGATIVE_ZERO))
|
||
{
|
||||
m_integer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_integer = std::abs(m_integer);
|
||||
}
|
||||
}
|
||||
// This is modeled after std::signbit() for floating point types
|
||||
constexpr inline bool signbit() const
|
||||
{
|
||||
return (m_integer < 0 || m_integer == NEGATIVE_ZERO);
|
||||
}
|
||||
constexpr inline void negate()
|
||||
{
|
||||
| 495f615f | David Sorber | if (__unlikely(m_integer == NEGATIVE_ZERO))
|
||
{
|
||||
m_integer = 0;
|
||||
}
|
||||
else if (__unlikely(m_integer == 0))
|
||||
| b9f341b6 | David Sorber | {
|
||
m_integer = NEGATIVE_ZERO;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_integer *= -1;
|
||||
}
|
||||
| 92782e4a | David Sorber | }
|
||
| b9f341b6 | David Sorber | |||
// Disallow conversion operators (for now)
|
||||
explicit operator int() = delete;
|
||||
explicit operator float() = delete;
|
||||
explicit operator double() = delete;
|
||||
| 92782e4a | 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
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend constexpr inline bool operator==(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend constexpr inline bool operator!=(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend constexpr inline bool operator<(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend constexpr inline bool operator>(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend constexpr inline bool operator<=(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
| c7e0510c | David Sorber | |||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend constexpr inline bool operator>=(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
| c7e0510c | David Sorber | |||
| 30849ea7 | David Sorber | // Multiply by integer
|
||
template<typename I, typename F, typename STI, typename STF>
|
||||
friend constexpr inline fixed<I, F, STI, STF> operator*(
|
||||
const fixed<I, F, STI, STF>& x, const int64_t& y);
|
||||
// Divide by integer
|
||||
| 92782e4a | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
friend constexpr inline fixed<I, F, STI, STF> operator/(
|
||||
| b9f341b6 | David Sorber | const fixed<I, F, STI, STF>& x, const int64_t& y);
|
||
| 92782e4a | David Sorber | |||
| 11ae9ac5 | David Sorber | // Output stream operator
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | friend inline std::ostream& operator<<(
|
||
| 852d0468 | David Sorber | std::ostream& os, const fixed<I, F, STI, STF>& rhs);
|
||
| c7e0510c | David Sorber | |||
| 92782e4a | David Sorber | |||
| 835f4592 | David Sorber | private:
|
||
| 852d0468 | David Sorber | static StrToIntegerTypeFunc strto_inttype;
|
||
static StrToFracTypeFunc strto_fractype;
|
||||
| 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());
|
||
}
|
||||
| 495f615f | David Sorber | else if (__unlikely(integerVal < MIN_INTEGER_VALUE))
|
||
{
|
||||
std::ostringstream msg;
|
||||
msg << "Integer value: " << integerVal << " exceeds minimum "
|
||||
<< "integer range of type (" << MIN_INTEGER_VALUE << ")!";
|
||||
throw std::out_of_range(msg.str());
|
||||
}
|
||||
| 11ae9ac5 | David Sorber | 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;
|
||||
}
|
||||
| 92782e4a | David Sorber | |||
// Helper function to get the approprate scale value for an unscaled input
|
||||
// value of the fractional type
|
||||
static inline FractionalType getFracScaleValue(
|
||||
FractionalType value,
|
||||
uint32_t leadingZeros=0)
|
||||
{
|
||||
uint32_t index = fractional_decimal_digits -
|
||||
(DIGIT_LOOKUP_TABLE.upper_bound(value)->second +
|
||||
leadingZeros);
|
||||
return SCALE_VALUES[index];
|
||||
}
|
||||
| 11ae9ac5 | David Sorber | |||
| 835f4592 | David Sorber | IntegerType m_integer;
|
||
FractionalType m_fractional;
|
||||
| 495f615f | David Sorber | |||
#ifndef CYBERLYNX
|
||||
friend class boost::serialization::access;
|
||||
template <typename Archive>
|
||||
void serialize(Archive &ar, const unsigned int version)
|
||||
{
|
||||
ar & m_integer;
|
||||
ar & m_fractional;
|
||||
}
|
||||
#endif
|
||||
| 835f4592 | David Sorber | };
|
||
| 852d0468 | David Sorber | // Functor for calling std::strtoll()
|
||
struct strtoll_ftor {
|
||||
inline int64_t operator()(const char* str, char** str_end, int base)
|
||||
{
|
||||
return std::strtoll(str, str_end, base);
|
||||
}
|
||||
};
|
||||
// Functor for calling std::strtoull()
|
||||
struct strtoull_ftor {
|
||||
inline uint64_t operator()(const char* str, char** str_end, int base)
|
||||
{
|
||||
return std::strtoull(str, str_end, base);
|
||||
}
|
||||
};
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
// Functor for strtoll_128()
|
||||
struct strto128_ftor {
|
||||
inline int128_t operator()(const char* str, char** str_end, int base)
|
||||
{
|
||||
return strtoll_128_b10opt(str, str_end, base);
|
||||
}
|
||||
};
|
||||
// Functor for calling strtoull_128
|
||||
struct strtou128_ftor {
|
||||
inline uint128_t operator()(const char* str, char** str_end, int base)
|
||||
{
|
||||
return strtoull_128_b10opt(str, str_end, base);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
| 11ae9ac5 | David Sorber | // Predefined types
|
||
| 852d0468 | David Sorber | using fixed_8_8 = fixed<int8_t, uint8_t, strtoll_ftor, strtoull_ftor>;
|
||
using fixed_8_16 = fixed<int8_t, uint16_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_8_32 = fixed<int8_t, uint32_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_8_64 = fixed<int8_t, uint64_t, strtoll_ftor, strtoull_ftor>;
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
using fixed_8_128 = fixed<int8_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||||
#endif
|
||||
| 852d0468 | David Sorber | |||
using fixed_16_8 = fixed<int16_t, uint8_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_16_16 = fixed<int16_t, uint16_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_16_32 = fixed<int16_t, uint32_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_16_64 = fixed<int16_t, uint64_t, strtoll_ftor, strtoull_ftor>;
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
using fixed_16_128 = fixed<int16_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||||
#endif
|
||||
| 852d0468 | David Sorber | |||
using fixed_32_8 = fixed<int32_t, uint8_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_32_16 = fixed<int32_t, uint16_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_32_32 = fixed<int32_t, uint32_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_32_64 = fixed<int32_t, uint64_t, strtoll_ftor, strtoull_ftor>;
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
using fixed_32_128 = fixed<int32_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||||
#endif
|
||||
| 852d0468 | David Sorber | |||
using fixed_64_8 = fixed<int64_t, uint8_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_64_16 = fixed<int64_t, uint16_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_64_32 = fixed<int64_t, uint32_t, strtoll_ftor, strtoull_ftor>;
|
||||
using fixed_64_64 = fixed<int64_t, uint64_t, strtoll_ftor, strtoull_ftor>;
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
using fixed_64_128 = fixed<int64_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||||
#endif
|
||||
#if SUPPORT_128_INTS
|
||||
using fixed_128_8 = fixed<int128_t, uint8_t, strto128_ftor, strtoull_ftor>;
|
||||
using fixed_128_16 = fixed<int128_t, uint16_t, strto128_ftor, strtoull_ftor>;
|
||||
using fixed_128_32 = fixed<int128_t, uint32_t, strto128_ftor, strtoull_ftor>;
|
||||
using fixed_128_64 = fixed<int128_t, uint64_t, strto128_ftor, strtoull_ftor>;
|
||||
using fixed_128_128 = fixed<int128_t, uint128_t, strto128_ftor, strtou128_ftor>;
|
||||
#endif
|
||||
| 852d0468 | David Sorber | |||
| 11ae9ac5 | David Sorber | // Precomputed scale value constants
|
||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
template<typename I, typename F, typename STI, typename STF>
|
||||
const uint128_t fixed<I, F, STI, STF>::SCALE_VALUES[39] =
|
||||
{
|
||||
/* 0 */ 1_u128,
|
||||
/* 1 */ 10_u128,
|
||||
/* 2 */ 100_u128,
|
||||
/* 3 */ 1000_u128,
|
||||
/* 4 */ 10000_u128,
|
||||
/* 5 */ 100000_u128,
|
||||
/* 6 */ 1000000_u128,
|
||||
/* 7 */ 10000000_u128,
|
||||
/* 8 */ 100000000_u128,
|
||||
/* 9 */ 1000000000_u128,
|
||||
/* 10 */ 10000000000_u128,
|
||||
/* 11 */ 100000000000_u128,
|
||||
/* 12 */ 1000000000000_u128,
|
||||
/* 13 */ 10000000000000_u128,
|
||||
/* 14 */ 100000000000000_u128,
|
||||
/* 15 */ 1000000000000000_u128,
|
||||
/* 16 */ 10000000000000000_u128,
|
||||
/* 17 */ 100000000000000000_u128,
|
||||
/* 18 */ 1000000000000000000_u128,
|
||||
/* 19 */ 10000000000000000000_u128,
|
||||
/* 20 */ 100000000000000000000_u128,
|
||||
/* 21 */ 1000000000000000000000_u128,
|
||||
/* 22 */ 10000000000000000000000_u128,
|
||||
/* 23 */ 100000000000000000000000_u128,
|
||||
/* 24 */ 1000000000000000000000000_u128,
|
||||
/* 25 */ 10000000000000000000000000_u128,
|
||||
/* 26 */ 100000000000000000000000000_u128,
|
||||
/* 27 */ 1000000000000000000000000000_u128,
|
||||
/* 28 */ 10000000000000000000000000000_u128,
|
||||
/* 29 */ 100000000000000000000000000000_u128,
|
||||
/* 30 */ 1000000000000000000000000000000_u128,
|
||||
/* 31 */ 10000000000000000000000000000000_u128,
|
||||
/* 32 */ 100000000000000000000000000000000_u128,
|
||||
/* 33 */ 1000000000000000000000000000000000_u128,
|
||||
/* 34 */ 10000000000000000000000000000000000_u128,
|
||||
/* 35 */ 100000000000000000000000000000000000_u128,
|
||||
/* 36 */ 1000000000000000000000000000000000000_u128,
|
||||
/* 37 */ 10000000000000000000000000000000000000_u128,
|
||||
/* 38 */ 100000000000000000000000000000000000000_u128,
|
||||
};
|
||||
#else
|
||||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
const uint64_t fixed<I, F, STI, STF>::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
|
||||
};
|
||||
| 495f615f | David Sorber | #endif
|
||
| 11ae9ac5 | David Sorber | |||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
template<typename I, typename F, typename STI, typename STF>
|
||||
const std::map<uint128_t, uint32_t> fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE{
|
||||
{1_u128, 0},
|
||||
{10_u128, 1},
|
||||
{100_u128, 2},
|
||||
{1000_u128, 3},
|
||||
{10000_u128, 4},
|
||||
{100000_u128, 5},
|
||||
{1000000_u128, 6},
|
||||
{10000000_u128, 7},
|
||||
{100000000_u128, 8},
|
||||
{1000000000_u128, 9},
|
||||
{10000000000_u128, 10},
|
||||
{100000000000_u128, 11},
|
||||
{1000000000000_u128, 12},
|
||||
{10000000000000_u128, 13},
|
||||
{100000000000000_u128, 14},
|
||||
{1000000000000000_u128, 15},
|
||||
{10000000000000000_u128, 16},
|
||||
{100000000000000000_u128, 17},
|
||||
{1000000000000000000_u128, 18},
|
||||
{10000000000000000000_u128, 19},
|
||||
{100000000000000000000_u128, 20},
|
||||
{1000000000000000000000_u128, 21},
|
||||
{10000000000000000000000_u128, 22},
|
||||
{100000000000000000000000_u128, 23},
|
||||
{1000000000000000000000000_u128, 24},
|
||||
{10000000000000000000000000_u128, 25},
|
||||
{100000000000000000000000000_u128, 26},
|
||||
{1000000000000000000000000000_u128, 27},
|
||||
{10000000000000000000000000000_u128, 28},
|
||||
{100000000000000000000000000000_u128, 29},
|
||||
{1000000000000000000000000000000_u128, 30},
|
||||
{10000000000000000000000000000000_u128, 31},
|
||||
{100000000000000000000000000000000_u128, 32},
|
||||
{1000000000000000000000000000000000_u128, 33},
|
||||
{10000000000000000000000000000000000_u128, 34},
|
||||
{100000000000000000000000000000000000_u128, 35},
|
||||
{1000000000000000000000000000000000000_u128, 36},
|
||||
{10000000000000000000000000000000000000_u128, 37},
|
||||
{std::numeric_limits<uint128_t>::max(), 38} // Special case for uint128_t max value
|
||||
};
|
||||
#else
|
||||
| 92782e4a | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
const std::map<uint64_t, uint32_t> fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE{
|
||||
{1ULL, 0},
|
||||
{10ULL, 1},
|
||||
{100ULL, 2},
|
||||
{1000ULL, 3},
|
||||
{10000ULL, 4},
|
||||
{100000ULL, 5},
|
||||
{1000000ULL, 6},
|
||||
{10000000ULL, 7},
|
||||
{100000000ULL, 8},
|
||||
{1000000000ULL, 9},
|
||||
{10000000000ULL, 10},
|
||||
{100000000000ULL, 11},
|
||||
{1000000000000ULL, 12},
|
||||
{10000000000000ULL, 13},
|
||||
{100000000000000ULL, 14},
|
||||
{1000000000000000ULL, 15},
|
||||
{10000000000000000ULL, 16},
|
||||
{100000000000000000ULL, 17},
|
||||
{1000000000000000000ULL, 18},
|
||||
{std::numeric_limits<uint64_t>::max(), 19} // Special case for uint64_t max value
|
||||
};
|
||||
| 495f615f | David Sorber | #endif
|
||
| 92782e4a | David Sorber | |||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | constexpr inline bool operator==(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
| 835f4592 | David Sorber | {
|
||
return (x.m_integer == y.m_integer && x.m_fractional == y.m_fractional);
|
||||
}
|
||||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | constexpr inline bool operator!=(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
| 835f4592 | David Sorber | {
|
||
| 11ae9ac5 | David Sorber | return (! (x == y));
|
||
| 835f4592 | David Sorber | }
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | constexpr inline bool operator<(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
| 835f4592 | David Sorber | {
|
||
| b9f341b6 | David Sorber | if (x.signbit() && (! y.signbit()))
|
||
| 835f4592 | David Sorber | {
|
||
| b9f341b6 | David Sorber | return true;
|
||
}
|
||||
else if ((! x.signbit()) && y.signbit())
|
||||
{
|
||||
return false;
|
||||
| 835f4592 | David Sorber | }
|
||
else
|
||||
{
|
||||
| b9f341b6 | David Sorber | I integerX = x.m_integer;
|
||
if (__unlikely((x.m_integer == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
integerX = 0;
|
||||
}
|
||||
I integerY = y.m_integer;
|
||||
if (__unlikely((y.m_integer == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
integerY = 0;
|
||||
}
|
||||
if (integerX == integerY)
|
||||
{
|
||||
| 495f615f | David Sorber | // Consider sign of integer portion
|
||
if (integerX < 0)
|
||||
{
|
||||
return (x.m_fractional > y.m_fractional);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (x.m_fractional < y.m_fractional);
|
||||
}
|
||||
| b9f341b6 | David Sorber | }
|
||
else
|
||||
{
|
||||
return (integerX < integerY);
|
||||
}
|
||||
| 835f4592 | David Sorber | }
|
||
| b9f341b6 | David Sorber | |||
| c7e0510c | David Sorber | return false;
|
||
| 835f4592 | David Sorber | }
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | constexpr inline bool operator>(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
| 835f4592 | David Sorber | {
|
||
| b9f341b6 | David Sorber | if (x.signbit() && (! y.signbit()))
|
||
{
|
||||
return false;
|
||||
}
|
||||
else if ((! x.signbit()) && y.signbit())
|
||||
| 835f4592 | David Sorber | {
|
||
| b9f341b6 | David Sorber | return true;
|
||
| 835f4592 | David Sorber | }
|
||
else
|
||||
{
|
||||
| b9f341b6 | David Sorber | I integerX = x.m_integer;
|
||
if (__unlikely((x.m_integer == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
integerX = 0;
|
||||
}
|
||||
I integerY = y.m_integer;
|
||||
if (__unlikely((y.m_integer == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
integerY = 0;
|
||||
}
|
||||
if (integerX == integerY)
|
||||
{
|
||||
| 495f615f | David Sorber | // Consider sign of integer portion
|
||
if (integerX < 0)
|
||||
{
|
||||
return (x.m_fractional < y.m_fractional);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (x.m_fractional > y.m_fractional);
|
||||
}
|
||||
| b9f341b6 | David Sorber | }
|
||
else
|
||||
{
|
||||
return (integerX > integerY);
|
||||
}
|
||||
| 835f4592 | David Sorber | }
|
||
| b9f341b6 | David Sorber | |||
| c7e0510c | David Sorber | return false;
|
||
| 835f4592 | David Sorber | }
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | constexpr inline bool operator<=(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
| 835f4592 | David Sorber | {
|
||
| b9f341b6 | David Sorber | return (x == y || x < y);
|
||
| 835f4592 | David Sorber | }
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
| c7e0510c | David Sorber | constexpr inline bool operator>=(
|
||
| 852d0468 | David Sorber | const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
| 835f4592 | David Sorber | {
|
||
| b9f341b6 | David Sorber | return (x == y || x > y);
|
||
| 835f4592 | David Sorber | }
|
||
| 11ae9ac5 | David Sorber | // Addition -- not yet supported
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
constexpr inline fixed<I, F, STI, STF> operator+(
|
||||
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 11ae9ac5 | David Sorber | // Subtraction -- not yet supported
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
constexpr inline fixed<I, F, STI, STF> operator-(
|
||||
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 92782e4a | David Sorber | // Multiplication -- not yet supported
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
constexpr inline fixed<I, F, STI, STF> operator*(
|
||||
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 11ae9ac5 | David Sorber | // Division -- not yet supported
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
constexpr inline fixed<I, F, STI, STF> operator/(
|
||||
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
|
||||
| 835f4592 | David Sorber | |||
| 30849ea7 | David Sorber | // Multiplication by integer
|
||
template<typename I, typename F, typename STI, typename STF>
|
||||
constexpr inline fixed<I, F, STI, STF> operator*(
|
||||
const fixed<I, F, STI, STF>& x, const int64_t& y)
|
||||
{
|
||||
| ef18eb33 | David Sorber | // Set flag to indicate if result should be negative
|
||
bool negativeResultFlag = false;
|
||||
I integerX = x.m_integer;
|
||||
if (__unlikely((integerX == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
integerX = 0;
|
||||
negativeResultFlag = true;
|
||||
}
|
||||
else if (integerX < 0)
|
||||
{
|
||||
integerX *= -1;
|
||||
negativeResultFlag = true;
|
||||
}
|
||||
| 30849ea7 | David Sorber | |||
| ef18eb33 | David Sorber | int64_t multiplier = y;
|
||
if (multiplier < 0)
|
||||
{
|
||||
// Ensure that multiplier is positive to avoid issues with
|
||||
// signed/unsigned operations
|
||||
multiplier *= -1;
|
||||
// Toggle negative results flag
|
||||
negativeResultFlag = !negativeResultFlag;
|
||||
}
|
||||
| 30849ea7 | David Sorber | |||
| ef18eb33 | David Sorber | // Multiply integer portion first
|
||
fixed<I, F, STI, STF> newVal;
|
||||
newVal.m_integer = integerX * multiplier;
|
||||
| 30849ea7 | David Sorber | |||
| ef18eb33 | David Sorber | uint32_t indexUpper = (fixed<I, F, STI, STF>::fractional_decimal_digits / 2);
|
||
uint32_t indexLower = (fixed<I, F, STI, STF>::fractional_decimal_digits - indexUpper);
|
||||
| 30849ea7 | David Sorber | |||
#if SUPPORT_128_INTS
|
||||
| ef18eb33 | David Sorber | uint128_t scalerUpper = fixed<I, F, STI, STF>::SCALE_VALUES[indexUpper];
|
||
uint128_t scalerLower = fixed<I, F, STI, STF>::SCALE_VALUES[indexLower];
|
||||
| 30849ea7 | David Sorber | #else
|
||
| ef18eb33 | David Sorber | uint64_t scalerUpper = fixed<I, F, STI, STF>::SCALE_VALUES[indexUpper];
|
||
uint64_t scalerLower = fixed<I, F, STI, STF>::SCALE_VALUES[indexLower];
|
||||
| 30849ea7 | David Sorber | #endif
|
||
| ef18eb33 | David Sorber | |||
// Compute the lower portion
|
||||
F fracLower = (x.m_fractional % scalerLower) * multiplier;
|
||||
// Compute lower carry explicitly as it may trigger an additional carry,
|
||||
// then rescale lower portion to remove carry
|
||||
F carryLower = fracLower / scalerLower;
|
||||
fracLower %= scalerLower;
|
||||
| 30849ea7 | David Sorber | |||
| ef18eb33 | David Sorber | // Compute upper portion
|
||
F fracUpper = ((x.m_fractional / scalerLower) * multiplier) + carryLower;
|
||||
newVal.m_fractional = (fracUpper % scalerUpper) * scalerLower;
|
||||
| 30849ea7 | David Sorber | |||
| ef18eb33 | David Sorber | // Computer integer carry then add it to integer portion
|
||
newVal.m_integer += (fracUpper / scalerUpper);
|
||||
| 30849ea7 | David Sorber | |||
| ef18eb33 | David Sorber | // Finally add the lower fractional portion, which at this point will not
|
||
// trigger a carry
|
||||
| 30849ea7 | David Sorber | newVal.m_fractional += fracLower;
|
||
| ef18eb33 | David Sorber | // If negative result flag was set ensure that final result is negative
|
||
if (negativeResultFlag)
|
||||
{
|
||||
if (newVal.m_integer == 0 && newVal.m_fractional > 0)
|
||||
{
|
||||
newVal.m_integer = fixed<I, F, STI, STF>::NEGATIVE_ZERO;
|
||||
}
|
||||
else if (newVal.m_integer > 0)
|
||||
{
|
||||
newVal.m_integer *= -1;
|
||||
}
|
||||
}
|
||||
| 30849ea7 | David Sorber | return newVal;
|
||
}
|
||||
// Division by integer
|
||||
| 92782e4a | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
constexpr inline fixed<I, F, STI, STF> operator/(
|
||||
| b9f341b6 | David Sorber | const fixed<I, F, STI, STF>& x, const int64_t& y)
|
||
{
|
||||
fixed<I, F, STI, STF> newVal;
|
||||
bool negativeDivisorFlag = (y < 0);
|
||||
bool negativeDividendFlag = (x.m_integer < 0) ||
|
||||
(x.m_integer == fixed<I, F, STI, STF>::NEGATIVE_ZERO);
|
||||
bool negativeZeroFlag = false;
|
||||
I integerX = x.m_integer;
|
||||
if (__unlikely((integerX == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
integerX = 0;
|
||||
negativeZeroFlag = true;
|
||||
}
|
||||
newVal.m_integer = integerX / y;
|
||||
newVal.m_fractional = std::abs(integerX) % y;
|
||||
newVal.m_fractional *= x.getFracScaleValue(newVal.m_fractional);
|
||||
newVal.m_fractional += x.m_fractional / std::abs(y);
|
||||
// Ensure quotient has correct sign value
|
||||
if (newVal.m_integer == 0 && negativeDividendFlag && negativeDivisorFlag)
|
||||
{
|
||||
newVal.m_integer = 0;
|
||||
}
|
||||
else if ((newVal.m_integer == 0 && (negativeDividendFlag || negativeDivisorFlag))
|
||||
|| __unlikely(negativeZeroFlag))
|
||||
| 92782e4a | David Sorber | {
|
||
| b9f341b6 | David Sorber | newVal.m_integer = fixed<I, F, STI, STF>::NEGATIVE_ZERO;
|
||
| 92782e4a | David Sorber | }
|
||
| b9f341b6 | David Sorber | |||
return newVal;
|
||||
}
|
||||
| 92782e4a | David Sorber | |||
| 11ae9ac5 | David Sorber | // Stream output
|
||
| 852d0468 | David Sorber | template<typename I, typename F, typename STI, typename STF>
|
||
inline std::ostream& operator<<(std::ostream& os, const fixed<I, F, STI, STF>& rhs)
|
||||
| 11ae9ac5 | David Sorber | {
|
||
| 92782e4a | David Sorber | #if 0
|
||
// Print full fractional precision always (even with trailing zeros)
|
||||
| c7e0510c | David Sorber | return os << std::fixed << rhs.m_integer << "."
|
||
| 852d0468 | David Sorber | << std::setw(fixed<I, F, STI, STF>::fractional_decimal_digits)
|
||
| c7e0510c | David Sorber | << std::setfill('0') << rhs.m_fractional;
|
||
| 92782e4a | David Sorber | #else
|
||
// Print fractional part without trailing zeros
|
||||
char buffer[fixed<I, F, STI, STF>::fractional_decimal_digits + 1]{};
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
// TODO: figure out better way to do this
|
||||
std::ostringstream msg;
|
||||
msg << rhs.m_fractional;
|
||||
std::memcpy(buffer, msg.str().c_str(), msg.str().size());
|
||||
#else
|
||||
| 92782e4a | David Sorber | std::snprintf(buffer, fixed<I, F, STI, STF>::fractional_decimal_digits + 1,
|
||
"%" PRIu64, rhs.m_fractional);
|
||||
| 495f615f | David Sorber | #endif
|
||
| 92782e4a | David Sorber | |||
// NOTE: if fractional part is zero always print at least one zero
|
||||
int idx = fixed<I, F, STI, STF>::fractional_decimal_digits;
|
||||
bool found = false;
|
||||
for (; idx >= 0; --idx)
|
||||
{
|
||||
if (std::isdigit(buffer[idx]) && buffer[idx] != '0')
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
| b9f341b6 | David Sorber | if (found || idx == -1)
|
||
| 92782e4a | David Sorber | {
|
||
buffer[idx + 1] = '\0';
|
||||
}
|
||||
| 495f615f | David Sorber | #if SUPPORT_128_INTS
|
||
uint32_t leadingZeros = (rhs.m_fractional == 0_u128) ? 1 :
|
||||
#else
|
||||
| b9f341b6 | David Sorber | uint32_t leadingZeros = (rhs.m_fractional == 0) ? 1 :
|
||
| 495f615f | David Sorber | #endif
|
||
| 92782e4a | David Sorber | fixed<I, F, STI, STF>::fractional_decimal_digits -
|
||
fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE.upper_bound(rhs.m_fractional)->second;
|
||||
| b9f341b6 | David Sorber | |||
// Handle special case of negative zero
|
||||
os << std::fixed;
|
||||
if (__unlikely((rhs.m_integer == fixed<I, F, STI, STF>::NEGATIVE_ZERO)))
|
||||
{
|
||||
os << "-0.";
|
||||
}
|
||||
else
|
||||
{
|
||||
os << rhs.m_integer << ".";
|
||||
}
|
||||
| 92782e4a | David Sorber | |||
| b9f341b6 | David Sorber | // Output any fractional leading zeros
|
||
| 92782e4a | David Sorber | for (uint32_t idx = 0; idx < leadingZeros; ++idx)
|
||
{
|
||||
os << "0";
|
||||
}
|
||||
return os << buffer;
|
||||
#endif
|
||||
| 11ae9ac5 | David Sorber | }
|
||
| c7e0510c | David Sorber | #endif // FIXED_H_
|