Project

General

Profile

Download (33.3 KB) Statistics
| Branch: | Tag: | Revision:
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
#include "Utilities/int128.h"
#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
92782e4a David Sorber
// Divide by unsigned int
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
92782e4a David Sorber
// Division by unsigned int
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_