Project

General

Profile

« Previous | Next » 

Revision 495f615f

Added by David Sorber about 4 years ago

Adding combined updates to the fixed point type.

View differences:

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;
software/fixed/int128.cc
#include "int128.h"
std::ostream& operator<<(std::ostream& dest, uint128_t value)
{
std::ostream::sentry check(dest);
if (check)
{
char buffer[128];
char* pos = std::end(buffer);
do
{
--pos;
*pos = "0123456789"[value % 10];
value /= 10;
} while (value != 0);
int len = std::end(buffer) - pos;
if (dest.rdbuf()->sputn(pos, len) != len)
{
dest.setstate(std::ios_base::badbit);
}
}
return dest;
}
std::ostream& operator<<(std::ostream& dest, int128_t value)
{
std::ostream::sentry check(dest);
if (check)
{
uint128_t tmp = value < 0 ? -value : value;
char buffer[128];
char* pos = std::end(buffer);
do
{
--pos;
*pos = "0123456789"[tmp % 10];
tmp /= 10;
} while (tmp != 0);
if (value < 0)
{
--pos;
*pos = '-';
}
int len = std::end(buffer) - pos;
if (dest.rdbuf()->sputn(pos, len) != len)
{
dest.setstate(std::ios_base::badbit);
}
}
return dest;
}
int128_t strtoll_128(const char* nptr, char** endptr, int base)
{
bool negative = false;
uint128_t cutoff = 0;
uint32_t cutlim = 0;
int128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-')
{
negative = true;
++ptr;
}
else if (*ptr == '+')
{
++ptr;
}
// Auto detect/validate base
if (*ptr == '0')
{
if ((base == 0 || base == 16) && std::toupper(ptr[1])== 'X')
{
ptr += 2;
base = 16;
}
else if (base == 0)
{
base = 8;
}
}
else if (base == 0)
{
base = 10;
}
// Save start of digits
save = ptr;
end = nullptr;
cutoff = max_tab[base - 2];;
cutlim = rem_tab[base - 2];
// Iterate over the characters
chr = *ptr;
for (; chr != '\0'; chr = *++ptr)
{
if (ptr == end)
{
break;
}
if (chr >= '0' && chr <= '9')
{
chr -= '0';
}
else if (std::isalpha(chr))
{
chr = std::toupper(chr) - 'A' + 10;
}
else
{
break;
}
if (static_cast<int>(chr) >= base)
{
break;
}
if (value > static_cast<int128_t>(cutoff) ||
(value == static_cast<int128_t>(cutoff) && chr > cutlim))
{
overflow = true;
break;
}
else
{
value *= static_cast<uint128_t>(base);
value += (int)chr;
}
}
// Check if no conversion was done
if (ptr == save)
{
goto noconv;
}
// Store end if requested
if (endptr != nullptr)
{
*endptr = const_cast<char*>(ptr);
}
// Check for overflow
if (__builtin_expect((overflow), 0))
{
std::ostringstream msg;
msg << "Overflow detected at input offset " << (ptr - nptr);
throw std::overflow_error(msg.str());
}
// Regular return path
return negative ? -value : value;
noconv:
if (endptr != nullptr)
{
if (save - nptr >= 2 && std::toupper(save[-1]) == 'X' && save[-2] == '0')
{
*endptr = const_cast<char*>(&save[-1]);
}
else
{
*endptr = const_cast<char*>(nptr);
}
}
return 0;
}
uint128_t strtoull_128(const char* nptr, char** endptr, int base)
{
uint128_t cutoff = 0;
uint32_t cutlim = 0;
uint128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-' || *ptr == '+')
{
++ptr;
}
// Auto detect/validate base
if (*ptr == '0')
{
if ((base == 0 || base == 16) && std::toupper(ptr[1])== 'X')
{
ptr += 2;
base = 16;
}
else if (base == 0)
{
base = 8;
}
}
else if (base == 0)
{
base = 10;
}
// Save start of digits
save = ptr;
end = nullptr;
cutoff = max_tab[base - 2];;
cutlim = rem_tab[base - 2];
// Iterate over the characters
chr = *ptr;
for (; chr != '\0'; chr = *++ptr)
{
if (ptr == end)
{
break;
}
if (chr >= '0' && chr <= '9')
{
chr -= '0';
}
else if (std::isalpha(chr))
{
chr = std::toupper(chr) - 'A' + 10;
}
else
{
break;
}
if (static_cast<int>(chr) >= base)
{
break;
}
if (value > cutoff || (value == cutoff && chr > cutlim))
{
overflow = true;
break;
}
else
{
value *= static_cast<uint128_t>(base);
value += (int)chr;
}
}
// Check if no conversion was done
if (ptr == save)
{
goto noconv;
}
// Store end if requested
if (endptr != nullptr)
{
*endptr = const_cast<char*>(ptr);
}
// Check for overflow
if (__builtin_expect((overflow), 0))
{
std::ostringstream msg;
msg << "Overflow detected at input offset " << (ptr - nptr);
throw std::overflow_error(msg.str());
}
// Regular return path
return value;
noconv:
if (endptr != nullptr)
{
if (save - nptr >= 2 && std::toupper(save[-1]) == 'X' && save[-2] == '0')
{
*endptr = const_cast<char*>(&save[-1]);
}
else
{
*endptr = const_cast<char*>(nptr);
}
}
return 0;
}
int128_t strtoll_128_b10opt(const char* nptr, char** endptr, int base)
{
bool negative = false;
uint128_t cutoff = 0;
uint32_t cutlim = 0;
int128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
#if 0
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
#endif
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-')
{
negative = true;
++ptr;
}
else if (*ptr == '+')
{
++ptr;
}
// Save start of digits
save = ptr;
end = nullptr;
cutoff = max_tab[8]; // base 10
cutlim = rem_tab[8];
// Iterate over the characters
chr = *ptr;
for (; chr != '\0'; chr = *++ptr)
{
if (ptr == end)
{
break;
}
if (chr >= '0' && chr <= '9')
{
chr -= '0';
}
else
{
break;
}
if (value > static_cast<int128_t>(cutoff) ||
(value == static_cast<int128_t>(cutoff) && chr > cutlim))
{
overflow = true;
break;
}
else
{
value *= static_cast<uint128_t>(10);
value += (int)chr;
}
}
// Check if no conversion was done
if (ptr == save)
{
goto noconv;
}
// Store end if requested
if (endptr != nullptr)
{
*endptr = const_cast<char*>(ptr);
}
// Check for overflow
if (__builtin_expect((overflow), 0))
{
std::ostringstream msg;
msg << "Overflow detected at input offset " << (ptr - nptr);
throw std::overflow_error(msg.str());
}
// Regular return path
return negative ? -value : value;
noconv:
if (endptr != nullptr)
{
*endptr = const_cast<char*>(nptr);
}
return 0;
}
uint128_t strtoull_128_b10opt(const char* nptr, char** endptr, int base)
{
uint128_t cutoff = 0;
uint32_t cutlim = 0;
uint128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
#if 0
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
#endif
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-' || *ptr == '+')
{
++ptr;
}
// Save start of digits
save = ptr;
end = nullptr;
cutoff = max_tab[8]; // base 10
cutlim = rem_tab[8];
// Iterate over the characters
chr = *ptr;
for (; chr != '\0'; chr = *++ptr)
{
if (ptr == end)
{
break;
}
if (chr >= '0' && chr <= '9')
{
chr -= '0';
}
else
{
break;
}
if (value > cutoff || (value == cutoff && chr > cutlim))
{
overflow = true;
break;
}
else
{
value *= static_cast<uint128_t>(10);
value += (int)chr;
}
}
// Check if no conversion was done
if (ptr == save)
{
goto noconv;
}
// Store end if requested
if (endptr != nullptr)
{
*endptr = const_cast<char*>(ptr);
}
// Check for overflow
if (__builtin_expect((overflow), 0))
{
std::ostringstream msg;
msg << "Overflow detected at input offset " << (ptr - nptr);
throw std::overflow_error(msg.str());
}
// Regular return path
return value;
noconv:
if (endptr != nullptr)
{
*endptr = const_cast<char*>(nptr);
}
return 0;
}
software/fixed/int128.h
typedef unsigned __int128 uint128_t;
std::ostream& operator<<(std::ostream& dest, uint128_t value)
{
std::ostream::sentry check(dest);
if (check)
{
char buffer[128];
char* pos = std::end(buffer);
do
{
--pos;
*pos = "0123456789"[value % 10];
value /= 10;
} while (value != 0);
int len = std::end(buffer) - pos;
if (dest.rdbuf()->sputn(pos, len) != len)
{
dest.setstate(std::ios_base::badbit);
}
}
return dest;
}
std::ostream& operator<<(std::ostream& dest, uint128_t value);
std::ostream& operator<<(std::ostream& dest, int128_t value)
{
std::ostream::sentry check(dest);
if (check)
{
uint128_t tmp = value < 0 ? -value : value;
char buffer[128];
char* pos = std::end(buffer);
do
{
--pos;
*pos = "0123456789"[tmp % 10];
tmp /= 10;
} while (tmp != 0);
if (value < 0)
{
--pos;
*pos = '-';
}
int len = std::end(buffer) - pos;
if (dest.rdbuf()->sputn(pos, len) != len)
{
dest.setstate(std::ios_base::badbit);
}
}
return dest;
}
std::ostream& operator<<(std::ostream& dest, int128_t value);
constexpr uint128_t max_tab[]
{
......
std::numeric_limits<uint128_t>::max() / 33,
std::numeric_limits<uint128_t>::max() / 34,
std::numeric_limits<uint128_t>::max() / 35,
std::numeric_limits<uint128_t>::max() / 36
std::numeric_limits<uint128_t>::max() / 36,
std::numeric_limits<uint128_t>::max() / 37,
std::numeric_limits<uint128_t>::max() / 38
};
constexpr uint32_t rem_tab[]
......
std::numeric_limits<uint128_t>::max() % 33,
std::numeric_limits<uint128_t>::max() % 34,
std::numeric_limits<uint128_t>::max() % 35,
std::numeric_limits<uint128_t>::max() % 36
std::numeric_limits<uint128_t>::max() % 36,
std::numeric_limits<uint128_t>::max() % 37,
std::numeric_limits<uint128_t>::max() % 38
};
int128_t strtoll_128(const char* nptr, char** endptr, int base)
{
bool negative = false;
uint128_t cutoff = 0;
uint32_t cutlim = 0;
int128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-')
{
negative = true;
++ptr;
}
else if (*ptr == '+')
{
++ptr;
}
// Auto detect/validate base
if (*ptr == '0')
{
if ((base == 0 || base == 16) && std::toupper(ptr[1])== 'X')
{
ptr += 2;
base = 16;
}
else if (base == 0)
{
base = 8;
}
}
else if (base == 0)
{
base = 10;
}
// Save start of digits
save = ptr;
end = nullptr;
cutoff = max_tab[base - 2];;
cutlim = rem_tab[base - 2];
// Iterate over the characters
chr = *ptr;
for (; chr != '\0'; chr = *++ptr)
{
if (ptr == end)
{
break;
}
if (chr >= '0' && chr <= '9')
{
chr -= '0';
}
else if (std::isalpha(chr))
{
chr = std::toupper(chr) - 'A' + 10;
}
else
{
break;
}
if (static_cast<int>(chr) >= base)
{
break;
}
if (value > cutoff || (value == cutoff && chr > cutlim))
{
overflow = true;
break;
}
else
{
value *= static_cast<uint128_t>(base);
value += (int)chr;
}
}
// Check if no conversion was done
if (ptr == save)
{
goto noconv;
}
// Store end if requested
if (endptr != nullptr)
{
*endptr = const_cast<char*>(ptr);
}
// Check for overflow
if (__builtin_expect((overflow), 0))
{
std::ostringstream msg;
msg << "Overflow detected at input offset " << (ptr - nptr);
throw std::overflow_error(msg.str());
}
// Regular return path
return negative ? -value : value;
noconv:
if (endptr != nullptr)
{
if (save - nptr >= 2 && std::toupper(save[-1]) == 'X' && save[-2] == '0')
{
*endptr = const_cast<char*>(&save[-1]);
}
else
{
*endptr = const_cast<char*>(nptr);
}
}
return 0;
int128_t strtoll_128(const char* nptr, char** endptr, int base);
uint128_t strtoull_128(const char* nptr, char** endptr, int base);
int128_t strtoll_128_b10opt(const char* nptr, char** endptr, int base=10);
uint128_t strtoull_128_b10opt(const char* nptr, char** endptr, int base=10);
// Source: https://github.com/jbapple/128-bit-literals/blob/master/suffix128.hpp
namespace suffix128 {
// Returns the value of the hexadecimal character
constexpr unsigned __int128 CharValue(char c) {
return (c >= '0' && c <= '9')
? (c - '0')
: ((c >= 'a' && c <= 'f') ? (10 + (c - 'a')) : (10 + (c - 'A')));
}
static constexpr unsigned __int128 MAX128 = ~static_cast<unsigned __int128>(0);
uint128_t strtoull_128(const char* nptr, char** endptr, int base)
{
uint128_t cutoff = 0;
uint32_t cutlim = 0;
uint128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-' || *ptr == '+')
{
++ptr;
}
// Auto detect/validate base
if (*ptr == '0')
{
if ((base == 0 || base == 16) && std::toupper(ptr[1])== 'X')
{
ptr += 2;
base = 16;
}
else if (base == 0)
{
base = 8;
}
}
else if (base == 0)
{
base = 10;
}
// Save start of digits
save = ptr;
end = nullptr;
cutoff = max_tab[base - 2];;
cutlim = rem_tab[base - 2];
// Iterate over the characters
chr = *ptr;
for (; chr != '\0'; chr = *++ptr)
{
if (ptr == end)
{
break;
}
if (chr >= '0' && chr <= '9')
{
chr -= '0';
}
else if (std::isalpha(chr))
{
chr = std::toupper(chr) - 'A' + 10;
}
else
{
break;
}
if (static_cast<int>(chr) >= base)
{
break;
}
if (value > cutoff || (value == cutoff && chr > cutlim))
{
overflow = true;
break;
}
else
{
value *= static_cast<uint128_t>(base);
value += (int)chr;
}
}
// Check if no conversion was done
if (ptr == save)
{
goto noconv;
}
// Store end if requested
if (endptr != nullptr)
{
*endptr = const_cast<char*>(ptr);
}
// Check for overflow
if (__builtin_expect((overflow), 0))
{
std::ostringstream msg;
msg << "Overflow detected at input offset " << (ptr - nptr);
throw std::overflow_error(msg.str());
}
// Regular return path
return value;
noconv:
if (endptr != nullptr)
{
if (save - nptr >= 2 && std::toupper(save[-1]) == 'X' && save[-2] == '0')
{
*endptr = const_cast<char*>(&save[-1]);
}
else
{
*endptr = const_cast<char*>(nptr);
}
}
return 0;
// ValidateU128Helper<BASE, c1, c2, ... , cn>(v) returns true iff cn + ... + c2
// * BASE^(n-2) + c1 * BASE^(n-1) + v * BASE^n is a valid 128-bit unsigned
// number when interpreted in base BASE.
template <int BASE>
constexpr bool ValidateU128Helper(unsigned __int128) {
return true;
}
template <int BASE, char C, char... CS>
constexpr bool ValidateU128Helper(unsigned __int128 accumulate) {
return (C == '\'') ? ValidateU128Helper<BASE, CS...>(accumulate)
: ((accumulate <= MAX128 / BASE) &&
(BASE * accumulate <= MAX128 - CharValue(C)) &&
ValidateU128Helper<BASE, CS...>(accumulate * BASE +
CharValue(C)));
}
int128_t strtoll_128_b10opt(const char* nptr, char** endptr, int base=10)
{
bool negative = false;
uint128_t cutoff = 0;
uint32_t cutlim = 0;
int128_t value = 0;
const char* ptr;
unsigned char chr;
const char *save, *end;
bool overflow = false;
// Validate the base parameter
#if 0
if (base < 0 || base == 1 || base > 36)
{
std::ostringstream msg;
msg << "Base value \"" << base << "\" is invalid; base must be "
<< "between 2-36 (inclusive).";
throw std::out_of_range(msg.str());
}
#endif
save = ptr = nptr;
// Skip any leading whitespace
while (std::isspace(*ptr))
{
++ptr;
}
// Check for end of string
if (__builtin_expect((*ptr == '\0'), 0))
{
goto noconv;
}
// Parse -/+
if (*ptr == '-')
{
negative = true;
++ptr;
}
else if (*ptr == '+')
{
++ptr;
}
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff