Project

General

Profile

« Previous | Next » 

Revision c7e0510c

Added by David Sorber over 4 years ago

Fix minor bug in int128.h. Also add initial unit tests.

View differences:

software/fixed/fixed.h
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <cstdint>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <stdexcept>
#include <type_traits>
#if __GNUC__ >= 3
# define __unlikely(cond) __builtin_expect ((cond), 0)
# define __likely(cond) __builtin_expect ((cond), 1)
#define __unlikely(cond) __builtin_expect((cond), 0)
#define __likely(cond) __builtin_expect((cond), 1)
#else
# define __unlikely(cond) (cond)
# define __likely(cond) (cond)
#define __unlikely(cond) (cond)
#define __likely(cond) (cond)
#endif
template <typename IntegerType, typename FractionalType>
class fixed
template<typename IntegerType, typename FractionalType>
class fixed
{
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");
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");
public:
static constexpr size_t integer_bits = sizeof(IntegerType) * 8;
static constexpr size_t fractional_bits = sizeof(FractionalType) * 8;
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()));
static constexpr size_t MAX_INTEGER_VALUE = (static_cast<uint64_t>(std::pow(10, integer_decimal_digits)) - 1);
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()));
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 size_t MAX_FRACTIONAL_VALUE = (static_cast<uint64_t>(std::pow(10, fractional_decimal_digits)) - 1);
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 const uint64_t SCALE_VALUES[20];
// Constructors
fixed()
: m_integer(0),
m_fractional(0)
: m_integer(0),
m_fractional(0)
{}
fixed(IntegerType integerVal)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(0)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(0)
{}
fixed(IntegerType integerVal, FractionalType fractionalVal)
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(__checkFracOverflow(fractionalVal))
: m_integer(__checkIntOverflow(integerVal)),
m_fractional(__checkFracOverflow(fractionalVal))
{
// Scale the fractional value appropriately
uint32_t idx = 0;
......
break;
}
}
m_fractional = __checkFracOverflow(m_fractional *
SCALE_VALUES[fractional_decimal_digits - idx]);
m_fractional = __checkFracOverflow(
m_fractional * SCALE_VALUES[fractional_decimal_digits - idx]);
}
// Default copy constructor, and assignment operator
fixed(const fixed&) = default;
fixed& operator=(const fixed&) = default;
fixed(const fixed&) = default;
fixed& operator=(const fixed&) = default;
// Reassign value to type
void assign(IntegerType integerVal, FractionalType fractionalVal)
{
......
break;
}
}
m_fractional = __checkFracOverflow(fractionalVal *
SCALE_VALUES[fractional_decimal_digits - idx]);
m_fractional = __checkFracOverflow(
fractionalVal * SCALE_VALUES[fractional_decimal_digits - idx]);
}
// Parse value from string input
uint32_t parse(const char* input)
{
char* endPtr = nullptr;
m_integer = __checkIntOverflow(strtoull(input, &endPtr, 10));
m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
m_fractional = 0;
if (std::isdigit(*endPtr))
{
throw std::out_of_range("Integer value is out of range.");
}
// If the ending char is a period we can now parse the fractional part
if (*endPtr == '.')
{
char* fracEndPtr = nullptr;
FractionalType fracTemp = __checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
FractionalType fracTemp =
__checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
uint32_t fracLen = (fracEndPtr - endPtr) - 1;
// DEBUG
//~ std::cout << "frac len: " << fracLen << std::endl;
fracLen = fractional_decimal_digits - fracLen;
//~ std::cout << "invt len: " << fracLen << std::endl;
m_fractional = __checkFracOverflow(fracTemp * SCALE_VALUES[fracLen]);
endPtr = fracEndPtr;
}
// TODO: could calculate and return overall length
// Calculate and return overall length
return (endPtr - input);
}
constexpr inline fixed operator-() const noexcept = delete;
constexpr inline fixed operator!() const noexcept = delete;
constexpr inline fixed operator~() const noexcept = delete;
inline fixed& operator+=(const fixed& y) noexcept = delete;
inline fixed& operator-=(const fixed& y) noexcept = delete;
inline fixed& operator*=(const fixed& y) noexcept = delete;
inline fixed& operator/=(const fixed& y) noexcept = delete;
inline fixed& operator/=(const fixed& y) noexcept = delete;
// Comparison operators
template <typename I, typename F>
friend constexpr inline bool operator==(const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template <typename I, typename F>
friend constexpr inline bool operator!=(const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template <typename I, typename F>
friend constexpr inline bool operator<(const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template <typename I, typename F>
friend constexpr inline bool operator>(const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template <typename I, typename F>
friend constexpr inline bool operator<=(const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template <typename I, typename F>
friend constexpr inline bool operator>=(const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template<typename I, typename F>
friend constexpr inline bool operator==(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template<typename I, typename F>
friend constexpr inline bool operator!=(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template<typename I, typename F>
friend constexpr inline bool operator<(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template<typename I, typename F>
friend constexpr inline bool operator>(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template<typename I, typename F>
friend constexpr inline bool operator<=(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
template<typename I, typename F>
friend constexpr inline bool operator>=(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
// Output stream operator
template <typename I, typename F>
friend inline std::ostream& operator<<(std::ostream& os, const fixed<I, F>& rhs);
template<typename I, typename F>
friend inline std::ostream& operator<<(
std::ostream& os, const fixed<I, F>& rhs);
private:
static inline IntegerType __checkIntOverflow(IntegerType integerVal)
......
{
std::ostringstream msg;
msg << "Integer value: " << integerVal << " exceeds maximum "
"integer range of type (" << MAX_INTEGER_VALUE << ")!";
<< "integer range of type (" << MAX_INTEGER_VALUE << ")!";
throw std::out_of_range(msg.str());
}
return integerVal;
}
static inline FractionalType __checkFracOverflow(FractionalType fractionalVal)
{
if (__unlikely(fractionalVal > MAX_FRACTIONAL_VALUE))
{
std::ostringstream msg;
msg << "Fractional value: " << fractionalVal << " exceeds maximum "
"fractional range of type (" << MAX_FRACTIONAL_VALUE << ")!";
<< "fractional range of type (" << MAX_FRACTIONAL_VALUE << ")!";
throw std::out_of_range(msg.str());
}
return fractionalVal;
......
IntegerType m_integer;
FractionalType m_fractional;
};
// Predefined types
......
using fixed_64_32 = fixed<int64_t, uint32_t>;
using fixed_64_64 = fixed<int64_t, uint64_t>;
// Precomputed scale value constants
template <typename I, typename F>
const uint64_t fixed<I, F>::SCALE_VALUES[20] =
template<typename I, typename F>
const uint64_t fixed<I, F>::SCALE_VALUES[20] =
{
/* 0 */ 1ULL,
/* 1 */ 10ULL,
/* 2 */ 100ULL,
/* 0 */ 1ULL,
/* 1 */ 10ULL,
/* 2 */ 100ULL,
/* 3 */ 1000ULL,
/* 4 */ 10000ULL,
/* 5 */ 100000ULL,
......
/* 19 */ 10000000000000000000ULL
};
template <typename I, typename F>
constexpr inline bool operator==(const fixed<I, F>& x, const fixed<I, F>& y) noexcept
template<typename I, typename F>
constexpr inline bool operator==(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
{
return (x.m_integer == y.m_integer && x.m_fractional == y.m_fractional);
}
template <typename I, typename F>
constexpr inline bool operator!=(const fixed<I, F>& x, const fixed<I, F>& y) noexcept
template<typename I, typename F>
constexpr inline bool operator!=(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
{
return (! (x == y));
}
template <typename I, typename F>
constexpr inline bool operator<(const fixed<I, F>& x, const fixed<I, F>& y) noexcept
template<typename I, typename F>
constexpr inline bool operator<(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
{
if (x.m_integer == y.m_integer)
{
......
{
return (x.m_integer < y.m_integer);
}
return false;
return false;
}
template <typename I, typename F>
constexpr inline bool operator>(const fixed<I, F>& x, const fixed<I, F>& y) noexcept
template<typename I, typename F>
constexpr inline bool operator>(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
{
if (x.m_integer == y.m_integer)
{
......
{
return (x.m_integer > y.m_integer);
}
return false;
return false;
}
template <typename I, typename F>
constexpr inline bool operator<=(const fixed<I, F>& x, const fixed<I, F>& y) noexcept
template<typename I, typename F>
constexpr inline bool operator<=(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
{
if (x.m_integer == y.m_integer)
{
......
{
return (x.m_integer < y.m_integer);
}
return false;
return false;
}
template <typename I, typename F>
constexpr inline bool operator>=(const fixed<I, F>& x, const fixed<I, F>& y) noexcept
template<typename I, typename F>
constexpr inline bool operator>=(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept
{
if (x.m_integer == y.m_integer)
{
......
{
return (x.m_integer > y.m_integer);
}
return false;
}
// 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>
constexpr inline fixed<I, F> operator+(
const fixed<I, F>& x, const fixed<I, F>& 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>
constexpr inline fixed<I, F> operator-(
const fixed<I, F>& x, const fixed<I, F>& 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>
constexpr inline fixed<I, F> operator*(
const fixed<I, F>& x, const fixed<I, F>& 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>
constexpr inline fixed<I, F> operator/(
const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
// Stream output
template <typename I, typename F>
template<typename I, typename F>
inline std::ostream& operator<<(std::ostream& os, const fixed<I, F>& rhs)
{
return os << std::fixed << rhs.m_integer << "."
<< std::setw(fixed<I, F>::fractional_decimal_digits) << std::setfill('0')
<< rhs.m_fractional;
return os << std::fixed << rhs.m_integer << "."
<< std::setw(fixed<I, F>::fractional_decimal_digits)
<< std::setfill('0') << rhs.m_fractional;
}
#endif // FIXED_H_
#endif // FIXED_H_

Also available in: Unified diff