Revision 495f615f
Added by David Sorber about 4 years ago
| software/fixed/fixed.h | ||
|---|---|---|
|
#include <cstdio>
|
||
|
#include <cstdint>
|
||
|
#include <cstdlib>
|
||
|
#include <cstring>
|
||
|
#include <iomanip>
|
||
|
#include <iostream>
|
||
|
#include <limits>
|
||
|
#include <map>
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
#include <type_traits>
|
||
|
#include <map>
|
||
|
|
||
|
#ifndef CYBERLYNX
|
||
|
#include <boost/serialization/access.hpp>
|
||
|
#include <boost/serialization/base_object.hpp>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if __GNUC__ >= 3
|
||
| ... | ... | |
|
#define SUPPORT_PARSE_EXPONENT 1 // Default: enable
|
||
|
#endif
|
||
|
|
||
|
#ifndef SUPPORT_128_INTS
|
||
|
#define SUPPORT_128_INTS 1 // Default: enable
|
||
|
#endif
|
||
|
|
||
|
#if SUPPORT_128_INTS
|
||
|
#include "Utilities/int128.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
template<typename T>
|
||
|
constexpr T calculateMax(size_t decimal_digits)
|
||
|
{
|
||
| ... | ... | |
|
|
||
|
static constexpr IntegerType MAX_INTEGER_VALUE =
|
||
|
(calculateMax<IntegerType>(integer_decimal_digits));
|
||
|
static constexpr IntegerType MIN_INTEGER_VALUE = 0;
|
||
|
static constexpr IntegerType MIN_INTEGER_VALUE = -MAX_INTEGER_VALUE;
|
||
|
|
||
|
static constexpr FractionalType MAX_FRACTIONAL_VALUE =
|
||
|
(calculateMax<FractionalType>(fractional_decimal_digits));
|
||
| ... | ... | |
|
|
||
|
static constexpr IntegerType NEGATIVE_ZERO = MAX_INTEGER_VALUE + 2;
|
||
|
|
||
|
#if SUPPORT_128_INTS
|
||
|
static const uint128_t SCALE_VALUES[39];
|
||
|
static const std::map<uint128_t, uint32_t> DIGIT_LOOKUP_TABLE;
|
||
|
#else
|
||
|
static const uint64_t SCALE_VALUES[20];
|
||
|
static const std::map<uint64_t, uint32_t> DIGIT_LOOKUP_TABLE;
|
||
|
#endif
|
||
|
|
||
|
// Constructors
|
||
|
explicit fixed()
|
||
| ... | ... | |
|
// Fractional value is prescaled to fractional type precision
|
||
|
m_fractional = __checkFracOverflow(fractionalVal);
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// Assign a new value with an *unscaled* fractional part (NOTE: use the
|
||
|
// "leadingZeros" parameter to represent the number of leading zeros in the
|
||
| ... | ... | |
|
m_integer = __checkIntOverflow(strto_inttype(input, &endPtr, 10));
|
||
|
m_fractional = 0;
|
||
|
bool negativeZeroFlag = false;
|
||
|
|
||
|
|
||
|
if (std::isdigit(*endPtr))
|
||
|
{
|
||
|
throw std::out_of_range("Integer value is out of range.");
|
||
|
}
|
||
|
|
||
|
// Check for negative zero corner case with leading zero digit
|
||
|
if (__unlikely(m_integer == 0 && endPtr - input >= 2 && *(endPtr - 2) == '-'))
|
||
|
if (__unlikely(m_integer == 0 && ((endPtr - input) >= 2) &&
|
||
|
*(endPtr - 2) == '-'))
|
||
|
{
|
||
|
negativeZeroFlag = true;
|
||
|
}
|
||
|
|
||
|
// If the ending char is a period we can now parse the fractional part
|
||
|
uint32_t fracLen = 0;
|
||
|
if (*endPtr == '.'|| (endPtr[0] == '-' && endPtr[1] == '.'))
|
||
|
if (*endPtr == '.'|| ((endPtr[0] == '-' || endPtr[0] == '+') && endPtr[1] == '.'))
|
||
|
{
|
||
|
// Check for negative zero corner case without leading zero digit
|
||
|
if (__unlikely(endPtr[0] == '-'))
|
||
|
if (__unlikely(endPtr[0] == '-'|| endPtr[0] == '+'))
|
||
|
{
|
||
|
negativeZeroFlag = (endPtr[0] == '-');
|
||
|
++endPtr;
|
||
|
negativeZeroFlag = true;
|
||
|
}
|
||
|
|
||
|
char* fracEndPtr = nullptr;
|
||
| ... | ... | |
|
m_integer *= static_cast<IntegerType>(SCALE_VALUES[exponent]);
|
||
|
}
|
||
|
|
||
|
uint64_t scaler = SCALE_VALUES[fractional_decimal_digits -
|
||
|
fracExponent];
|
||
|
FractionalType scaler = SCALE_VALUES[fractional_decimal_digits -
|
||
|
fracExponent];
|
||
|
IntegerType temp = m_fractional / scaler;
|
||
|
m_integer += temp;
|
||
|
if (m_integer < 0)
|
||
|
{
|
||
|
m_integer += -temp;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_integer += temp;
|
||
|
}
|
||
|
m_fractional -= (temp * scaler);
|
||
|
m_fractional *= SCALE_VALUES[fracExponent];
|
||
|
|
||
| ... | ... | |
|
else
|
||
|
{
|
||
|
// CASE 2: fractional zero
|
||
|
long intExponent = std::abs(exponent);
|
||
|
if (static_cast<size_t>(intExponent) > integer_decimal_digits)
|
||
|
size_t intExponent = -exponent;
|
||
|
if (intExponent > integer_decimal_digits)
|
||
|
{
|
||
|
intExponent = integer_decimal_digits;
|
||
|
}
|
||
|
|
||
|
temp = std::abs(m_integer) % SCALE_VALUES[intExponent];
|
||
|
temp *= SCALE_VALUES[fractional_decimal_digits - intExponent];
|
||
|
m_integer /= SCALE_VALUES[intExponent];
|
||
|
|
||
|
#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];
|
||
|
}
|
||
|
m_fractional += temp;
|
||
|
|
||
|
if (std::abs(exponent) > intExponent)
|
||
|
if (static_cast<size_t>(std::abs(exponent)) > intExponent)
|
||
|
{
|
||
|
long fracExponent = std::abs(exponent) - intExponent;
|
||
|
m_fractional /= SCALE_VALUES[fracExponent];
|
||
| ... | ... | |
|
{
|
||
|
m_integer = NEGATIVE_ZERO;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Calculate and return overall length
|
||
|
return (endPtr - input);
|
||
|
}
|
||
| ... | ... | |
|
|
||
|
constexpr inline void negate()
|
||
|
{
|
||
|
if (__unlikely(m_integer == 0))
|
||
|
if (__unlikely(m_integer == NEGATIVE_ZERO))
|
||
|
{
|
||
|
m_integer = 0;
|
||
|
}
|
||
|
else if (__unlikely(m_integer == 0))
|
||
|
{
|
||
|
m_integer = NEGATIVE_ZERO;
|
||
|
}
|
||
| ... | ... | |
|
<< "integer range of type (" << MAX_INTEGER_VALUE << ")!";
|
||
|
throw std::out_of_range(msg.str());
|
||
|
}
|
||
|
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());
|
||
|
}
|
||
|
|
||
|
return integerVal;
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
|
||
|
IntegerType m_integer;
|
||
|
FractionalType m_fractional;
|
||
|
|
||
|
#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
|
||
|
};
|
||
|
|
||
|
// Functor for calling std::strtoll()
|
||
| ... | ... | |
|
}
|
||
|
};
|
||
|
|
||
|
#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
|
||
|
|
||
|
// Predefined types
|
||
|
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>;
|
||
|
#if SUPPORT_128_INTS
|
||
|
using fixed_8_128 = fixed<int8_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||
|
#endif
|
||
|
|
||
|
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>;
|
||
|
#if SUPPORT_128_INTS
|
||
|
using fixed_16_128 = fixed<int16_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||
|
#endif
|
||
|
|
||
|
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>;
|
||
|
#if SUPPORT_128_INTS
|
||
|
using fixed_32_128 = fixed<int32_t, uint128_t, strtoll_ftor, strtou128_ftor>;
|
||
|
#endif
|
||
|
|
||
|
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>;
|
||
|
#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
|
||
|
|
||
|
// Precomputed scale value constants
|
||
|
#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
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
const uint64_t fixed<I, F, STI, STF>::SCALE_VALUES[20] =
|
||
|
{
|
||
| ... | ... | |
|
/* 18 */ 1000000000000000000ULL,
|
||
|
/* 19 */ 10000000000000000000ULL
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#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
|
||
|
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},
|
||
| ... | ... | |
|
{1000000000000000000ULL, 18},
|
||
|
{std::numeric_limits<uint64_t>::max(), 19} // Special case for uint64_t max value
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator==(
|
||
| ... | ... | |
|
|
||
|
if (integerX == integerY)
|
||
|
{
|
||
|
return (x.m_fractional < y.m_fractional);
|
||
|
// Consider sign of integer portion
|
||
|
if (integerX < 0)
|
||
|
{
|
||
|
return (x.m_fractional > y.m_fractional);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (x.m_fractional < y.m_fractional);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
| ... | ... | |
|
|
||
|
if (integerX == integerY)
|
||
|
{
|
||
|
return (x.m_fractional > y.m_fractional);
|
||
|
// Consider sign of integer portion
|
||
|
if (integerX < 0)
|
||
|
{
|
||
|
return (x.m_fractional < y.m_fractional);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (x.m_fractional > y.m_fractional);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
| ... | ... | |
|
#else
|
||
|
// Print fractional part without trailing zeros
|
||
|
char buffer[fixed<I, F, STI, STF>::fractional_decimal_digits + 1]{};
|
||
|
#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
|
||
|
std::snprintf(buffer, fixed<I, F, STI, STF>::fractional_decimal_digits + 1,
|
||
|
"%" PRIu64, rhs.m_fractional);
|
||
|
#endif
|
||
|
|
||
|
// NOTE: if fractional part is zero always print at least one zero
|
||
|
int idx = fixed<I, F, STI, STF>::fractional_decimal_digits;
|
||
| ... | ... | |
|
buffer[idx + 1] = '\0';
|
||
|
}
|
||
|
|
||
|
#if SUPPORT_128_INTS
|
||
|
uint32_t leadingZeros = (rhs.m_fractional == 0_u128) ? 1 :
|
||
|
#else
|
||
|
uint32_t leadingZeros = (rhs.m_fractional == 0) ? 1 :
|
||
|
#endif
|
||
|
fixed<I, F, STI, STF>::fractional_decimal_digits -
|
||
|
fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE.upper_bound(rhs.m_fractional)->second;
|
||
|
|
||
Adding combined updates to the fixed point type.