Project

General

Profile

« Previous | Next » 

Revision b9f341b6

Added by David Sorber over 4 years ago

Adding fixed point type changes from a few weeks ago. This includes
significantly expanded tests.

View differences:

software/fixed/fixed.h
#define __likely(cond) (cond)
#endif
// 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
template<typename T>
constexpr T calculateMax(size_t 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()));
static constexpr size_t total_decimal_digits = integer_decimal_digits +
fractional_decimal_digits;
static constexpr IntegerType MAX_INTEGER_VALUE =
(calculateMax<IntegerType>(integer_decimal_digits));
......
(calculateMax<FractionalType>(fractional_decimal_digits));
static constexpr FractionalType MIN_FRACTIONAL_VALUE = 0;
static constexpr IntegerType NEGATIVE_ZERO = MAX_INTEGER_VALUE + 2;
static const uint64_t SCALE_VALUES[20];
static const std::map<uint64_t, uint32_t> DIGIT_LOOKUP_TABLE;
// Constructors
fixed()
explicit fixed()
: m_integer(0),
m_fractional(0)
{}
fixed(IntegerType integerVal)
explicit fixed(IntegerType integerVal)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(0)
{}
fixed(IntegerType integerVal, FractionalType fractionalVal)
explicit fixed(IntegerType integerVal, FractionalType fractionalVal)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(__checkFracOverflow(fractionalVal))
{
......
getFracScaleValue(m_fractional));
}
fixed(IntegerType integerVal, FractionalType fractionalVal, uint32_t leadingZeros)
explicit fixed(IntegerType integerVal, FractionalType fractionalVal, uint32_t leadingZeros)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(__checkFracOverflow(fractionalVal))
{
......
char* endPtr = nullptr;
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) == '-'))
{
negativeZeroFlag = true;
}
// If the ending char is a period we can now parse the fractional part
if (*endPtr == '.')
uint32_t fracLen = 0;
if (*endPtr == '.'|| (endPtr[0] == '-' && endPtr[1] == '.'))
{
// Check for negative zero corner case without leading zero digit
if (__unlikely(endPtr[0] == '-'))
{
++endPtr;
negativeZeroFlag = true;
}
char* fracEndPtr = nullptr;
FractionalType fracTemp =
__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]);
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]);
}
endPtr = fracEndPtr;
}
#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]);
}
uint64_t scaler = SCALE_VALUES[fractional_decimal_digits -
fracExponent];
IntegerType temp = m_fractional / scaler;
m_integer += temp;
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
long intExponent = std::abs(exponent);
if (static_cast<size_t>(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];
m_fractional += temp;
if (std::abs(exponent) > intExponent)
{
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;
}
// Calculate and return overall length
return (endPtr - input);
}
void absval()
constexpr inline void absval()
{
m_integer = abs(m_integer);
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()
{
if (__unlikely(m_integer == 0))
{
m_integer = NEGATIVE_ZERO;
}
else
{
m_integer *= -1;
}
}
// Disallow conversion operators (for now)
explicit operator int() = delete;
explicit operator float() = delete;
explicit operator double() = delete;
constexpr inline fixed operator-() const noexcept = delete;
constexpr inline fixed operator!() const noexcept = delete;
......
// 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);
const fixed<I, F, STI, STF>& x, const int64_t& y);
// Output stream operator
template<typename I, typename F, typename STI, typename STF>
......
return fractionalVal;
}
public:
// Helper function to get the approprate scale value for an unscaled input
// value of the fractional type
static inline FractionalType getFracScaleValue(
......
return SCALE_VALUES[index];
}
public:
IntegerType m_integer;
FractionalType m_fractional;
};
......
constexpr inline bool operator<(
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
{
if (x.m_integer == y.m_integer)
if (x.signbit() && (! y.signbit()))
{
return (x.m_fractional < y.m_fractional);
return true;
}
else if ((! x.signbit()) && y.signbit())
{
return false;
}
else
{
return (x.m_integer < y.m_integer);
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)
{
return (x.m_fractional < y.m_fractional);
}
else
{
return (integerX < integerY);
}
}
return false;
}
......
constexpr inline bool operator>(
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
{
if (x.m_integer == y.m_integer)
if (x.signbit() && (! y.signbit()))
{
return false;
}
else if ((! x.signbit()) && y.signbit())
{
return (x.m_fractional > y.m_fractional);
return true;
}
else
{
return (x.m_integer > y.m_integer);
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)
{
return (x.m_fractional > y.m_fractional);
}
else
{
return (integerX > integerY);
}
}
return false;
}
......
constexpr inline bool operator<=(
const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
{
if (x.m_integer == y.m_integer)
{
return (x.m_fractional <= y.m_fractional);
}
else
{
return (x.m_integer < y.m_integer);
}
return false;
return (x == y || x < y);
}
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
{
if (x.m_integer == y.m_integer)
{
return (x.m_fractional >= y.m_fractional);
}
else
{
return (x.m_integer > y.m_integer);
}
return false;
return (x == y || x > y);
}
// Addition -- not yet supported
......
// 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)
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))
{
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;
newVal.m_integer = fixed<I, F, STI, STF>::NEGATIVE_ZERO;
}
return newVal;
}
// Stream output
template<typename I, typename F, typename STI, typename STF>
......
break;
}
}
if (found)
if (found || idx == -1)
{
buffer[idx + 1] = '\0';
}
uint32_t leadingZeros =
uint32_t leadingZeros = (rhs.m_fractional == 0) ? 1 :
fixed<I, F, STI, STF>::fractional_decimal_digits -
fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE.upper_bound(rhs.m_fractional)->second;
// 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 << ".";
}
// Output any leading zeros
os << std::fixed << rhs.m_integer << ".";
// Output any fractional leading zeros
for (uint32_t idx = 0; idx < leadingZeros; ++idx)
{
os << "0";

Also available in: Unified diff