Revision 852d0468
Added by David Sorber over 4 years ago
| software/fixed/fixed.h | ||
|---|---|---|
|
#include <stdexcept>
|
||
|
#include <type_traits>
|
||
|
|
||
|
#ifdef USE_INT128
|
||
|
#include "int128.h"
|
||
|
#endif
|
||
|
|
||
|
#if __GNUC__ >= 3
|
||
|
#define __unlikely(cond) __builtin_expect((cond), 0)
|
||
|
#define __likely(cond) __builtin_expect((cond), 1)
|
||
| ... | ... | |
|
#define __likely(cond) (cond)
|
||
|
#endif
|
||
|
|
||
|
template<typename IntegerType, typename FractionalType>
|
||
|
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>
|
||
|
class fixed
|
||
|
{
|
||
|
static_assert(std::is_integral<IntegerType>::value,
|
||
| ... | ... | |
|
static constexpr size_t fractional_decimal_digits =
|
||
|
std::floor(std::log10(std::numeric_limits<FractionalType>::max()));
|
||
|
|
||
|
static constexpr size_t MAX_INTEGER_VALUE =
|
||
|
(static_cast<uint64_t>(std::pow(10, integer_decimal_digits)) - 1);
|
||
|
static constexpr size_t MIN_INTEGER_VALUE = 0;
|
||
|
static constexpr IntegerType MAX_INTEGER_VALUE =
|
||
|
(calculateMax<IntegerType>(integer_decimal_digits));
|
||
|
static constexpr IntegerType MIN_INTEGER_VALUE = 0;
|
||
|
|
||
|
static constexpr size_t MAX_FRACTIONAL_VALUE =
|
||
|
(static_cast<uint64_t>(std::pow(10, fractional_decimal_digits)) - 1);
|
||
|
static constexpr size_t MIN_FRACTIONAL_VALUE = 0;
|
||
|
static constexpr FractionalType MAX_FRACTIONAL_VALUE =
|
||
|
(calculateMax<FractionalType>(fractional_decimal_digits));
|
||
|
static constexpr FractionalType MIN_FRACTIONAL_VALUE = 0;
|
||
|
|
||
|
static const uint64_t SCALE_VALUES[20];
|
||
|
|
||
| ... | ... | |
|
uint32_t parse(const char* input)
|
||
|
{
|
||
|
char* endPtr = nullptr;
|
||
|
m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
|
||
|
//~ m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
|
||
|
m_integer = __checkIntOverflow(strto_inttype(input, &endPtr, 10));
|
||
|
m_fractional = 0;
|
||
|
|
||
|
if (std::isdigit(*endPtr))
|
||
| ... | ... | |
|
{
|
||
|
char* fracEndPtr = nullptr;
|
||
|
FractionalType fracTemp =
|
||
|
__checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
|
||
|
//~ __checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
|
||
|
__checkFracOverflow(strto_fractype(endPtr + 1, &fracEndPtr, 10));
|
||
|
uint32_t fracLen = (fracEndPtr - endPtr) - 1;
|
||
|
|
||
|
fracLen = fractional_decimal_digits - fracLen;
|
||
| ... | ... | |
|
inline fixed& operator/=(const fixed& y) noexcept = delete;
|
||
|
|
||
|
// Comparison operators
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend constexpr inline bool operator==(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend constexpr inline bool operator!=(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend constexpr inline bool operator<(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend constexpr inline bool operator>(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend constexpr inline bool operator<=(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend constexpr inline bool operator>=(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
|
||
|
|
||
|
// Output stream operator
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
friend inline std::ostream& operator<<(
|
||
|
std::ostream& os, const fixed<I, F>& rhs);
|
||
|
std::ostream& os, const fixed<I, F, STI, STF>& rhs);
|
||
|
|
||
|
private:
|
||
|
|
||
|
static StrToIntegerTypeFunc strto_inttype;
|
||
|
static StrToFracTypeFunc strto_fractype;
|
||
|
|
||
|
static inline IntegerType __checkIntOverflow(IntegerType integerVal)
|
||
|
{
|
||
|
if (__unlikely(integerVal > MAX_INTEGER_VALUE))
|
||
| ... | ... | |
|
FractionalType m_fractional;
|
||
|
};
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#ifdef USE_INT128
|
||
|
|
||
|
// Functor for calling strtoll_128()
|
||
|
struct strtoll_128_ftor {
|
||
|
inline int64_t operator()(const char* str, char** str_end, int base)
|
||
|
{
|
||
|
//~ return strtoll_128(str, str_end, base);
|
||
|
return strtoll_128_b10opt(str, str_end, base);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Functor for calling strtoull_128()
|
||
|
struct strtoull_128_ftor {
|
||
|
inline uint64_t operator()(const char* str, char** str_end, int base)
|
||
|
{
|
||
|
//~ return strtoull_128(str, str_end, base);
|
||
|
return strtoull_128_b10opt(str, str_end, base);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Predefined types
|
||
|
using fixed_8_8 = fixed<int8_t, uint8_t>;
|
||
|
using fixed_8_16 = fixed<int8_t, uint16_t>;
|
||
|
using fixed_8_32 = fixed<int8_t, uint32_t>;
|
||
|
using fixed_8_64 = fixed<int8_t, uint64_t>;
|
||
|
|
||
|
using fixed_16_8 = fixed<int16_t, uint8_t>;
|
||
|
using fixed_16_16 = fixed<int16_t, uint16_t>;
|
||
|
using fixed_16_32 = fixed<int16_t, uint32_t>;
|
||
|
using fixed_16_64 = fixed<int16_t, uint64_t>;
|
||
|
|
||
|
using fixed_32_8 = fixed<int32_t, uint8_t>;
|
||
|
using fixed_32_16 = fixed<int32_t, uint16_t>;
|
||
|
using fixed_32_32 = fixed<int32_t, uint32_t>;
|
||
|
using fixed_32_64 = fixed<int32_t, uint64_t>;
|
||
|
|
||
|
using fixed_64_8 = fixed<int64_t, uint8_t>;
|
||
|
using fixed_64_16 = fixed<int64_t, uint16_t>;
|
||
|
using fixed_64_32 = fixed<int64_t, uint32_t>;
|
||
|
using fixed_64_64 = fixed<int64_t, uint64_t>;
|
||
|
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>;
|
||
|
|
||
|
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>;
|
||
|
|
||
|
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>;
|
||
|
|
||
|
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>;
|
||
|
|
||
|
#ifdef USE_INT128
|
||
|
using fixed_128_128 = fixed<int128_t, uint128_t, strtoll_128_ftor, strtoull_128_ftor>;
|
||
|
#endif
|
||
|
|
||
|
// Precomputed scale value constants
|
||
|
template<typename I, typename F>
|
||
|
const uint64_t fixed<I, F>::SCALE_VALUES[20] =
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
const uint64_t fixed<I, F, STI, STF>::SCALE_VALUES[20] =
|
||
|
{
|
||
|
/* 0 */ 1ULL,
|
||
|
/* 1 */ 10ULL,
|
||
| ... | ... | |
|
/* 19 */ 10000000000000000000ULL
|
||
|
};
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator==(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
|
{
|
||
|
return (x.m_integer == y.m_integer && x.m_fractional == y.m_fractional);
|
||
|
}
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator!=(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
|
{
|
||
|
return (! (x == y));
|
||
|
}
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator<(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
|
{
|
||
|
if (x.m_integer == y.m_integer)
|
||
|
{
|
||
| ... | ... | |
|
return false;
|
||
|
}
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator>(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
|
{
|
||
|
if (x.m_integer == y.m_integer)
|
||
|
{
|
||
| ... | ... | |
|
return false;
|
||
|
}
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator<=(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
|
{
|
||
|
if (x.m_integer == y.m_integer)
|
||
|
{
|
||
| ... | ... | |
|
return false;
|
||
|
}
|
||
|
|
||
|
template<typename I, typename F>
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
constexpr inline bool operator>=(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
|
||
|
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
|
||
|
{
|
||
|
if (x.m_integer == y.m_integer)
|
||
|
{
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// Addition -- not yet supported
|
||
|
template<typename I, typename F>
|
||
|
constexpr inline fixed<I, F> operator+(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||
|
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;
|
||
|
|
||
|
// Subtraction -- not yet supported
|
||
|
template<typename I, typename F>
|
||
|
constexpr inline fixed<I, F> operator-(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||
|
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;
|
||
|
|
||
|
// Multiplication
|
||
|
template<typename I, typename F>
|
||
|
constexpr inline fixed<I, F> operator*(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||
|
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;
|
||
|
|
||
|
// Division -- not yet supported
|
||
|
template<typename I, typename F>
|
||
|
constexpr inline fixed<I, F> operator/(
|
||
|
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
|
||
|
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;
|
||
|
|
||
|
// Stream output
|
||
|
template<typename I, typename F>
|
||
|
inline std::ostream& operator<<(std::ostream& os, const fixed<I, F>& rhs)
|
||
|
template<typename I, typename F, typename STI, typename STF>
|
||
|
inline std::ostream& operator<<(std::ostream& os, const fixed<I, F, STI, STF>& rhs)
|
||
|
{
|
||
|
return os << std::fixed << rhs.m_integer << "."
|
||
|
<< std::setw(fixed<I, F>::fractional_decimal_digits)
|
||
|
<< std::setw(fixed<I, F, STI, STF>::fractional_decimal_digits)
|
||
|
<< std::setfill('0') << rhs.m_fractional;
|
||
|
}
|
||
|
|
||
| software/fixed/fixed_util.cc | ||
|---|---|---|
|
{
|
||
|
std::cout << "Fixed Type Test Util\n" << std::endl;
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
fixed_64_64 foobar;
|
||
|
std::cout << "Type size: " << sizeof(fixed_64_64) << std::endl;
|
||
|
|
||
| ... | ... | |
|
std::cout << "Max fractional value: " << fixed_64_64::MAX_FRACTIONAL_VALUE << std::endl;
|
||
|
|
||
|
std::cout << std::endl;
|
||
|
std::cout << "Value: " << foobar << std::endl;
|
||
|
std::cout << "Value: " << foobar << "\n\n" << std::endl;
|
||
|
//-------------------------------------------------------------------------
|
||
|
#ifdef USE_INT128
|
||
|
|
||
|
fixed_128_128 barfoo;
|
||
|
std::cout << "Type size: " << sizeof(barfoo) << std::endl;
|
||
|
|
||
|
std::cout << "Integer decimal digits: " << fixed_128_128::integer_decimal_digits << std::endl;
|
||
|
std::cout << "Fractional decimal digits: " << fixed_128_128::fractional_decimal_digits << std::endl;
|
||
|
|
||
|
std::cout << "Max integer value: " << fixed_128_128::MAX_INTEGER_VALUE << std::endl;
|
||
|
|
||
|
std::cout << "Max fractional value: " << fixed_128_128::MAX_FRACTIONAL_VALUE << std::endl;
|
||
|
|
||
|
std::cout << std::endl;
|
||
|
std::cout << "Value: " << barfoo << "\n\n" << std::endl;
|
||
|
|
||
|
#endif
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
fixed_64_64 quxbar(3, 1415926353);
|
||
|
std::cout << "Value: " << quxbar << std::endl;
|
||
| software/fixed/parse_test.cc | ||
|---|---|---|
|
<< " ms" << std::endl;
|
||
|
}
|
||
|
#endif
|
||
|
// 5)
|
||
|
else if (parseType == 5)
|
||
|
{
|
||
|
std::cout << "Parse type 5: fixed_128_128" << std::endl;
|
||
|
auto start = std::chrono::system_clock::now();
|
||
|
for (auto& strValue : values)
|
||
|
{
|
||
|
fvalue.parse(strValue.c_str());
|
||
|
//~ std::cout << "In: " << strValue << " -- Out: " << fvalue << std::endl;
|
||
|
}
|
||
|
auto end = std::chrono::system_clock::now();
|
||
|
auto diff = end - start;
|
||
|
std::cout << "Total parse time: "
|
||
|
<< std::chrono::duration <double, std::milli>(diff).count()
|
||
|
<< " ms" << std::endl;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
std::cerr << "ERROR: invalid parse type: " << parseType << std::endl;
|
||
Modify template to include parameters for strtoXXX functions. Also begin
integrating support for 128 bit integer types.