Project

General

Profile

« Previous | Next » 

Revision 92782e4a

Added by David Sorber over 4 years ago

Additional improvements to the fixed type. Also added a few good test
cases.

View differences:

software/fixed/fixed.h
#ifndef __FIXED_H_
#define __FIXED_H_
#define __STDC_FORMAT_MACROS
#include <cctype>
#include <cmath>
#include <cinttypes>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <iomanip>
......
#include <sstream>
#include <stdexcept>
#include <type_traits>
#include <map>
#ifdef USE_INT128
#include "int128.h"
#endif
#if __GNUC__ >= 3
#define __unlikely(cond) __builtin_expect((cond), 0)
......
static constexpr FractionalType MIN_FRACTIONAL_VALUE = 0;
static const uint64_t SCALE_VALUES[20];
static const std::map<uint64_t, uint32_t> DIGIT_LOOKUP_TABLE;
// Constructors
fixed()
......
m_fractional(__checkFracOverflow(fractionalVal))
{
// Scale the fractional value appropriately
uint32_t idx = 0;
for (; idx < fractional_decimal_digits; ++idx)
{
if (SCALE_VALUES[idx] > m_fractional)
{
break;
}
}
m_fractional = __checkFracOverflow(
m_fractional * SCALE_VALUES[fractional_decimal_digits - idx]);
m_fractional = __checkFracOverflow(m_fractional *
getFracScaleValue(m_fractional));
}
fixed(IntegerType integerVal, FractionalType fractionalVal, uint32_t leadingZeros)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(__checkFracOverflow(fractionalVal))
{
// Scale the fractional value appropriately
m_fractional = __checkFracOverflow(m_fractional *
getFracScaleValue(m_fractional,
leadingZeros));
}
// Default copy constructor, and assignment operator
fixed(const fixed&) = default;
fixed& operator=(const fixed&) = default;
// Reassign value to type
// Reassign value to type with a prescaled fractional value.
void assign(IntegerType integerVal, FractionalType fractionalVal)
{
m_integer = __checkIntOverflow(integerVal);
// Scale the fractional value appropriately
uint32_t idx = 0;
for (; idx < fractional_decimal_digits; ++idx)
{
if (SCALE_VALUES[idx] > fractionalVal)
{
break;
}
}
// Fractional value is prescaled to fractional type precision
m_fractional = __checkFracOverflow(fractionalVal);
}
m_fractional = __checkFracOverflow(
fractionalVal * SCALE_VALUES[fractional_decimal_digits - idx]);
// 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));
}
// Parse value from string input
uint32_t parse(const char* input)
{
char* endPtr = nullptr;
//~ m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
m_integer = __checkIntOverflow(strto_inttype(input, &endPtr, 10));
m_fractional = 0;
......
{
char* fracEndPtr = nullptr;
FractionalType fracTemp =
//~ __checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
__checkFracOverflow(strto_fractype(endPtr + 1, &fracEndPtr, 10));
uint32_t fracLen = (fracEndPtr - endPtr) - 1;
fracLen = fractional_decimal_digits - fracLen;
m_fractional = __checkFracOverflow(fracTemp * SCALE_VALUES[fracLen]);
......
return (endPtr - input);
}
void absval()
{
m_integer = abs(m_integer);
}
constexpr inline fixed operator-() const noexcept = delete;
constexpr inline fixed operator!() const noexcept = delete;
constexpr inline fixed operator~() const noexcept = delete;
......
friend constexpr inline bool operator>=(
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
// Divide by unsigned int
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 uint64_t& y);
// Output stream operator
template<typename I, typename F, typename STI, typename STF>
friend inline std::ostream& operator<<(
std::ostream& os, const fixed<I, F, STI, STF>& rhs);
private:
static StrToIntegerTypeFunc strto_inttype;
......
}
return fractionalVal;
}
public:
// 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];
}
public:
IntegerType m_integer;
FractionalType m_fractional;
};
......
}
};
#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, strtoll_ftor, strtoull_ftor>;
using fixed_8_16 = fixed<int8_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, typename STI, typename STF>
const uint64_t fixed<I, F, STI, STF>::SCALE_VALUES[20] =
......
/* 19 */ 10000000000000000000ULL
};
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
};
template<typename I, typename F, typename STI, typename STF>
constexpr inline bool operator==(
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
......
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
// Multiplication -- not yet supported
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;
......
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 by unsigned int
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 uint64_t& y)
{
fixed<I, F, STI, STF> newVal;
newVal.m_integer = x.m_integer / y;
newVal.m_fractional = x.m_integer % y;
newVal.m_fractional *= x.getFracScaleValue(newVal.m_fractional);
newVal.m_fractional += x.m_fractional / y;
return newVal;
}
// Stream output
template<typename I, typename F, typename STI, typename STF>
inline std::ostream& operator<<(std::ostream& os, const fixed<I, F, STI, STF>& rhs)
{
#if 0
// Print full fractional precision always (even with trailing zeros)
return os << std::fixed << rhs.m_integer << "."
<< std::setw(fixed<I, F, STI, STF>::fractional_decimal_digits)
<< std::setfill('0') << rhs.m_fractional;
#else
// Print fractional part without trailing zeros
char buffer[fixed<I, F, STI, STF>::fractional_decimal_digits + 1]{};
std::snprintf(buffer, fixed<I, F, STI, STF>::fractional_decimal_digits + 1,
"%" PRIu64, rhs.m_fractional);
// 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;
}
}
if (found)
{
buffer[idx + 1] = '\0';
}
uint32_t leadingZeros =
fixed<I, F, STI, STF>::fractional_decimal_digits -
fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE.upper_bound(rhs.m_fractional)->second;
// Output any leading zeros
os << std::fixed << rhs.m_integer << ".";
for (uint32_t idx = 0; idx < leadingZeros; ++idx)
{
os << "0";
}
return os << buffer;
#endif
}
#endif // FIXED_H_

Also available in: Unified diff