Revision 7fa2f28e
Added by David Sorber over 4 years ago
| software/fixed/fixed.h | ||
|---|---|---|
|
|
||
|
#ifndef FIXED_H_
|
||
|
#define FIXED_H_
|
||
|
#ifndef __FIXED_H_
|
||
|
#define __FIXED_H_
|
||
|
|
||
|
#include <cctype>
|
||
|
#include <cmath>
|
||
| ... | ... | |
|
#include <sstream>
|
||
|
#include <type_traits>
|
||
|
|
||
|
#if __GNUC__ >= 3
|
||
|
# define __unlikely(cond) __builtin_expect ((cond), 0)
|
||
|
# define __likely(cond) __builtin_expect ((cond), 1)
|
||
|
#else
|
||
|
# define __unlikely(cond) (cond)
|
||
|
# define __likely(cond) (cond)
|
||
|
#endif
|
||
|
|
||
|
template <typename IntegerType, typename FractionalType>
|
||
|
class fixed
|
||
|
{
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// Parse value from string input
|
||
|
void parse(const char* input)
|
||
|
uint32_t parse(const char* input)
|
||
|
{
|
||
|
char* endPtr = nullptr;
|
||
|
m_integer = __checkIntOverflow(strtoull(input, &endPtr, 10));
|
||
| ... | ... | |
|
//~ std::cout << "invt len: " << fracLen << std::endl;
|
||
|
|
||
|
m_fractional = __checkFracOverflow(fracTemp * SCALE_VALUES[fracLen]);
|
||
|
endPtr = fracEndPtr;
|
||
|
}
|
||
|
// TODO: could calculate and return overall length
|
||
|
//~ uint32_t length = endPtr - input;
|
||
|
return (endPtr - input);
|
||
|
}
|
||
|
|
||
|
constexpr inline fixed operator-() const noexcept = delete;
|
||
| ... | ... | |
|
|
||
|
static inline IntegerType __checkIntOverflow(IntegerType integerVal)
|
||
|
{
|
||
|
if (integerVal > MAX_INTEGER_VALUE)
|
||
|
if (__unlikely(integerVal > MAX_INTEGER_VALUE))
|
||
|
{
|
||
|
std::ostringstream msg;
|
||
|
msg << "Integer value: " << integerVal << " exceeds maximum "
|
||
| ... | ... | |
|
|
||
|
static inline FractionalType __checkFracOverflow(FractionalType fractionalVal)
|
||
|
{
|
||
|
if (fractionalVal > MAX_FRACTIONAL_VALUE)
|
||
|
if (__unlikely(fractionalVal > MAX_FRACTIONAL_VALUE))
|
||
|
{
|
||
|
std::ostringstream msg;
|
||
|
msg << "Fractional value: " << fractionalVal << " exceeds maximum "
|
||
| software/fixed/fixed_test.cc | ||
|---|---|---|
|
#include <cmath>
|
||
|
#include <cstdint>
|
||
|
#include <limits>
|
||
|
#include <iomanip>
|
||
|
#include <iostream>
|
||
|
#include <stdexcept>
|
||
|
|
||
|
#include "fixed.h"
|
||
|
#include "int128.h"
|
||
|
|
||
|
// g++ fixed_test.cc -o fixed_test
|
||
|
int main(int argc, char** argv)
|
||
| ... | ... | |
|
//~ double foo = std::pow(10, 18);
|
||
|
//~ std::cout << "Foo: " << std::fixed << std::setprecision(0) << foo << std::endl;
|
||
|
//~ std::cout << "Foo: " << std::fixed << std::setprecision(0) << (static_cast<uint64_t>(foo) - 1) << std::endl;
|
||
|
|
||
|
|
||
|
uint128_t big = 1098298217598174ULL;
|
||
|
std::cout << "BIG INT: " << big << std::endl;
|
||
|
big *= 1098982694765;
|
||
|
std::cout << "BIGGER INT: " << big << std::endl;
|
||
|
|
||
|
std::cout << "INT128T MAX: " << std::numeric_limits<int128_t>::max() << std::endl;
|
||
|
std::cout << "UINT128T MAX: " << std::numeric_limits<uint128_t>::max() << std::endl;
|
||
|
|
||
|
strVal = "-1207010734831637608463359110";
|
||
|
int128_t s128Val = strtoll_128(strVal, nullptr, 0);
|
||
|
std::cout << "String value: " << strVal << std::endl;
|
||
|
std::cout << "Parsed value: " << s128Val << std::endl;
|
||
|
|
||
|
strVal = "991207010734831637608463359110888";
|
||
|
uint128_t u128Val = strtoull_128(strVal, nullptr, 0);
|
||
|
std::cout << "String value: " << strVal << std::endl;
|
||
|
std::cout << "Parsed value: " << u128Val << std::endl;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
| software/fixed/int128.h | ||
|---|---|---|
|
#ifndef __INT128_H_
|
||
|
#define __INT128_H_
|
||
|
|
||
|
#include <cctype>
|
||
|
#include <limits>
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
|
||
|
// NOTE: These 128 bit types are GCC specific; see:
|
||
|
// https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html
|
||
|
typedef __int128 int128_t;
|
||
|
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, 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;
|
||
|
}
|
||
|
|
||
|
constexpr uint128_t max_tab[]
|
||
|
{
|
||
|
std::numeric_limits<uint128_t>::max() / 2,
|
||
|
std::numeric_limits<uint128_t>::max() / 3,
|
||
|
std::numeric_limits<uint128_t>::max() / 4,
|
||
|
std::numeric_limits<uint128_t>::max() / 5,
|
||
|
std::numeric_limits<uint128_t>::max() / 6,
|
||
|
std::numeric_limits<uint128_t>::max() / 7,
|
||
|
std::numeric_limits<uint128_t>::max() / 8,
|
||
|
std::numeric_limits<uint128_t>::max() / 9,
|
||
|
std::numeric_limits<uint128_t>::max() / 11,
|
||
|
std::numeric_limits<uint128_t>::max() / 12,
|
||
|
std::numeric_limits<uint128_t>::max() / 13,
|
||
|
std::numeric_limits<uint128_t>::max() / 14,
|
||
|
std::numeric_limits<uint128_t>::max() / 15,
|
||
|
std::numeric_limits<uint128_t>::max() / 16,
|
||
|
std::numeric_limits<uint128_t>::max() / 17,
|
||
|
std::numeric_limits<uint128_t>::max() / 18,
|
||
|
std::numeric_limits<uint128_t>::max() / 19,
|
||
|
std::numeric_limits<uint128_t>::max() / 20,
|
||
|
std::numeric_limits<uint128_t>::max() / 21,
|
||
|
std::numeric_limits<uint128_t>::max() / 22,
|
||
|
std::numeric_limits<uint128_t>::max() / 23,
|
||
|
std::numeric_limits<uint128_t>::max() / 24,
|
||
|
std::numeric_limits<uint128_t>::max() / 25,
|
||
|
std::numeric_limits<uint128_t>::max() / 26,
|
||
|
std::numeric_limits<uint128_t>::max() / 27,
|
||
|
std::numeric_limits<uint128_t>::max() / 28,
|
||
|
std::numeric_limits<uint128_t>::max() / 29,
|
||
|
std::numeric_limits<uint128_t>::max() / 31,
|
||
|
std::numeric_limits<uint128_t>::max() / 32,
|
||
|
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
|
||
|
};
|
||
|
|
||
|
constexpr uint32_t rem_tab[]
|
||
|
{
|
||
|
std::numeric_limits<uint128_t>::max() % 2,
|
||
|
std::numeric_limits<uint128_t>::max() % 3,
|
||
|
std::numeric_limits<uint128_t>::max() % 4,
|
||
|
std::numeric_limits<uint128_t>::max() % 5,
|
||
|
std::numeric_limits<uint128_t>::max() % 6,
|
||
|
std::numeric_limits<uint128_t>::max() % 7,
|
||
|
std::numeric_limits<uint128_t>::max() % 8,
|
||
|
std::numeric_limits<uint128_t>::max() % 9,
|
||
|
std::numeric_limits<uint128_t>::max() % 11,
|
||
|
std::numeric_limits<uint128_t>::max() % 12,
|
||
|
std::numeric_limits<uint128_t>::max() % 13,
|
||
|
std::numeric_limits<uint128_t>::max() % 14,
|
||
|
std::numeric_limits<uint128_t>::max() % 15,
|
||
|
std::numeric_limits<uint128_t>::max() % 16,
|
||
|
std::numeric_limits<uint128_t>::max() % 17,
|
||
|
std::numeric_limits<uint128_t>::max() % 18,
|
||
|
std::numeric_limits<uint128_t>::max() % 19,
|
||
|
std::numeric_limits<uint128_t>::max() % 20,
|
||
|
std::numeric_limits<uint128_t>::max() % 21,
|
||
|
std::numeric_limits<uint128_t>::max() % 22,
|
||
|
std::numeric_limits<uint128_t>::max() % 23,
|
||
|
std::numeric_limits<uint128_t>::max() % 24,
|
||
|
std::numeric_limits<uint128_t>::max() % 25,
|
||
|
std::numeric_limits<uint128_t>::max() % 26,
|
||
|
std::numeric_limits<uint128_t>::max() % 27,
|
||
|
std::numeric_limits<uint128_t>::max() % 28,
|
||
|
std::numeric_limits<uint128_t>::max() % 29,
|
||
|
std::numeric_limits<uint128_t>::max() % 31,
|
||
|
std::numeric_limits<uint128_t>::max() % 32,
|
||
|
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
|
||
|
};
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
| software/fixed/parse_test.cc | ||
|---|---|---|
|
//~ std::mt19937 rng(dev());
|
||
|
std::mt19937 rng;
|
||
|
rng.seed(seed);
|
||
|
std::uniform_int_distribution<std::mt19937::result_type> numDigits(1, 20);
|
||
|
std::uniform_int_distribution<std::mt19937::result_type> numDigits(1, 18);
|
||
|
std::uniform_int_distribution<std::mt19937::result_type> digits(0, 9);
|
||
|
|
||
|
for (uint32_t idx = 0; idx < number; ++idx)
|
||
Adding int128.h header with support for GCC specific int128_t and
uint128_t types.