Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

323 lines
10 KiB
C++

#include "pch.h"
#include <Math/Int128.h>
#pragma warning(disable : 4073)
#pragma init_seg(lib)
const _int128 _I128_MIN( 0x8000000000000000, 0x0000000000000000);
const _int128 _I128_MAX( 0x7fffffffffffffff, 0xffffffffffffffff);
const _uint128 _UI128_MAX(0xffffffffffffffff, 0xffffffffffffffff);
// Conversion from _int128/_uint128 to string
// Number of digits that should be appended to the string for each loop.
// Also used as maximal number of digits to scan, that fits in 32 bit UINT
// digitCount[R] = floor(logR(_UI32_MAX+1))
// (index = radix = [2..36])
static const BYTE digitCount[37] = {
0, 0,32,20,16,13,12,11
,10,10, 9, 9, 8, 8, 8, 8
, 8, 7, 7, 7, 7, 7, 7, 7
, 6, 6, 6, 6, 6, 6, 6, 6
, 6, 6, 6, 6, 6
};
// Highest power of radix that fits in 32 bit (index = radix)
// For radix 2,4,8,16,32 special code is used which doesn't use this table.
// For all other values the element is used to get as many digits as possible
// by modulus and division. (powRadix[r] == pow(r,digitCount[r])
static const _uint128 powRadix[] = {
0 // not used
,0 // not used
,0 // not used
,0xcfd41b91 // 3486784401 = 3^20
,0 // not used
,0x48c27395 // 1220703125 = 5^13
,0x81bf1000 // 2176782336 = 6^12
,0x75db9c97 // 1977326743 = 7^11
,0 // not used
,0xcfd41b91 // 3486784401 = 9^10
,0x3b9aca00 // 1000000000 = 10^9
,0x8c8b6d2b // 2357947691 = 11^9
,0x19a10000 // 429981696 = 12^8
,0x309f1021 // 815730721 = 13^8
,0x57f6c100 // 1475789056 = 14^8
,0x98c29b81 // 2562890625 = 15^8
,0 // not used
,0x18754571 // 410338673 = 17^7
,0x247dbc80 // 612220032 = 18^7
,0x3547667b // 893871739 = 19^7
,0x4c4b4000 // 1280000000 = 20^7
,0x6b5a6e1d // 1801088541 = 21^7
,0x94ace180 // 2494357888 = 22^7
,0xcaf18367 // 3404825447 = 23^7
, 0xb640000 // 191102976 = 24^6
, 0xe8d4a51 // 244140625 = 25^6
,0x1269ae40 // 308915776 = 26^6
,0x17179149 // 387420489 = 27^6
,0x1cb91000 // 481890304 = 28^6
,0x23744899 // 594823321 = 29^6
,0x2b73a840 // 729000000 = 30^6
,0x34e63b41 // 887503681 = 31^6
,0 // not used
,0x4cfa3cc1 // 1291467969 = 33^6
,0x5c13d840 // 1544804416 = 34^6
,0x6d91b519 // 1838265625 = 35^6
,0x81bf1000 // 2176782336 = 36^6
};
#define ULTOSTR(v, str, radix) \
{ if(sizeof(Ctype) == sizeof(char)) \
_ultoa(v, (char*)str, radix); \
else \
_ultow(v, (wchar_t*)str, radix); \
}
#define STRLEN(str) ((sizeof(Ctype)==sizeof(char))?strlen((char*)str):wcslen((wchar_t*)str))
#define STRCPY(dst, src) ((sizeof(Ctype)==sizeof(char))?(Ctype*)strcpy((char*)dst, (char*)src):(Ctype*)wcscpy((wchar_t*)dst, (wchar_t*)src))
#define STRREV(str) ((sizeof(Ctype)==sizeof(char))?(Ctype*)_strrev((char*)str):(Ctype*)_wcsrev((wchar_t*)str))
template<class Int128Type, class Ctype> Ctype *int128toStr(Int128Type value, Ctype *str, UINT radix) {
if((radix < 2) || (radix > 36)) {
errno = EINVAL;
str[0] = 0;
return str;
}
errno = 0;
bool setSign = false;
if(value.isZero()) {
str[0] = '0';
str[1] = 0;
return str;
}
Ctype *s = str;
switch(radix) {
case 2 :
case 4 :
case 16:
{ const UINT dc = digitCount[radix];
for(int i = 3; i >= 0; i--) {
if(value.s4.i[i]) {
Ctype tmpStr[40];
ULTOSTR(value.s4.i[i], tmpStr, radix);
const size_t l = STRLEN(tmpStr);
if(s != str) {
for(size_t i = dc - l; i--;) *(s++) = '0'; // fill up with zeroes, if not leading digits
}
STRCPY(s, tmpStr);
s += l;
} else if(s != str) {
for(size_t i = dc; i--;) *(s++) = '0'; // fill up with zeroes, if not leading digits
}
}
*s = 0;
}
return str;
case 8 : // Get 3 bits/digit giving 30 bits/loop, ie 10 digits/loop
case 32: // Get 5 bits/digit giving 30 bits/loop too! which is 6 digits/loop
{ const UINT shft = (radix==32)?5:3;
const UINT mask = (1 << shft) - 1;
const UINT dpl = 30 / shft;
_uint128 v = value;
for(;;) {
UINT v30 = v.s4.i[0] & ((1<<30) - 1);
v >>= 30;
UINT count;
for(count = 0; v30; count++, v30 >>= shft) {
*(s++) = radixLetter(v30 & mask);
}
if(v.isZero()) break;
while(count++ < dpl) *(s++) = '0';
}
}
break;
case 10:
if(value.isNegative()) {
value = -value;
setSign = true;
}
// NB continue case
default:
const UINT dc = digitCount[radix];
const _uint128 &divisor = powRadix[radix];
_uint128 v = value;
for(;;) {
const UINT c = v % divisor;
Ctype tmpStr[40];
ULTOSTR(c, tmpStr, radix);
STRCPY(s, STRREV(tmpStr));
size_t l = STRLEN(tmpStr);
s += l;
v /= divisor;
if(v) {
while(l++ < dc) *(s++) = '0'; // append zeroes
} else {
break;
}
}
if(setSign) *(s++) = '-';
break;
}
*s = 0;
return STRREV(str);
}
char *_i128toa(_int128 value, char *str, int radix) {
return int128toStr<_int128, char>(value, str, radix);
}
wchar_t *_i128tow(_int128 value, wchar_t *str, int radix) {
return int128toStr<_int128, wchar_t>(value, str, radix);
}
char*_ui128toa(_uint128 value, char *str, int radix) {
return int128toStr<_uint128, char>(value, str, radix);
}
wchar_t *_ui128tow(_uint128 value, wchar_t *str, int radix) {
return int128toStr<_uint128, wchar_t>(value, str, radix);
}
// Conversion from string to _int128/_uint128
static inline bool isRadixDigit(wchar_t ch, UINT radix, UINT &value) {
if(!iswalnum(ch)) return false;
const UINT v = iswdigit(ch) ? (ch - '0') : (ch - (iswupper(ch)?'A':'a') + 10);
if(v >= radix) return false;
value = v;
return true;
}
template<class Int128Type, class Ctype, bool withSign> Int128Type strtoint128(const Ctype *s, Ctype **end, UINT radix, _locale_t locale) {
if((s == NULL) || ((radix != 0) && ((radix < 2) || (radix > 36)))) {
errno = EINVAL;
return 0;
}
errno = 0;
bool negative = false;
bool gotDigit = false;
while(iswspace(*s)) s++; // skip whitespace
if(*s == '-') { // read optional sign
s++;
negative = true;
} else if (*s == '+') {
s++;
}
UINT digit;
if(radix == 0) { // first determine if radix is 8,10 or 16
if(*s == '0') {
gotDigit = true;
s++;
if(end) *end = (Ctype*)s;
if((*s == 'x') || (*s == 'X')) {
radix = 16; s++;
} else {
radix = 8;
}
if(!isRadixDigit(*s, radix, digit)) { // we've scanned "0[x]",
return 0; // if no more digits, then 0
}
} else if(iswdigit(*s)) {
radix = 10;
} else {
return 0; // nothing recognized
}
}
Int128Type result128;
bool overflow = false;
const UINT maxDigitCount32 = digitCount[radix];
if(isRadixDigit(*(s++), radix, digit)) {
gotDigit = true;
while((digit == 0) && isRadixDigit(*s, radix, digit)) s++; // skip leading zeroes
bool firstChunk = true;
UINT result32 = digit;
if((radix & -(int)radix) == radix) { // is radix 2,4,8,16 or 32
const UINT maxBitCount = withSign ? 127 : 128;
UINT totalBitCount;
const UINT bitsPerDigit = 32 / maxDigitCount32;
const UINT maxBitCount32 = bitsPerDigit * maxDigitCount32;
for(UINT bitCount32=bitsPerDigit;;result32=bitCount32=0) {
while(isRadixDigit(*(s++), radix, digit)) {
result32 <<= bitsPerDigit;
result32 |= digit;
if((bitCount32 += bitsPerDigit) == maxBitCount32) break;
}
if(firstChunk) {
result128 = result32;
firstChunk = false;
totalBitCount = bitCount32;
} else if(bitCount32) {
if((totalBitCount += bitCount32) > maxBitCount) {
const BYTE mask = ~(((withSign&&!negative)?0x7f:0xff)>>(bitCount32&7));
if(result128.s16.b[15-(bitCount32>>3)] & mask) {
overflow = true;
if(bitCount32 == maxBitCount32) {
while(isRadixDigit(*(s++), radix, digit));
}
break;
}
}
result128 <<= bitCount32;
if(result32) result128 |= result32;
} else {
break;
}
if(bitCount32 < maxBitCount32) break;
}
} else {
for(UINT digitCount32=1, p32=radix;; p32=1, result32=digitCount32=0) {
while(isRadixDigit(*(s++), radix, digit)) {
result32 *= radix;
result32 += digit;
p32 *= radix;
if(++digitCount32 == maxDigitCount32) break;
}
if(firstChunk) {
result128 = result32;
firstChunk = false;
} else if(digitCount32) {
const UINT lastH = result128.s4.i[3];
result128 *= p32;
if(result32) result128 += result32;
if(lastH && ((result128.s4.i[3]==0) || (withSign&&!negative&&result128.isNegative()))) {
overflow = true;
if(digitCount32 == maxDigitCount32) {
while(isRadixDigit(*(s++), radix, digit));
}
break;
}
} else {
break;
}
if(digitCount32 < maxDigitCount32) break;
}
}
}
if(!gotDigit) return 0;
if(end) *end = (Ctype*)s-1;
if(overflow) {
errno = ERANGE;
return withSign ? (negative ? _I128_MIN : _I128_MAX) : _UI128_MAX;
}
return negative ? -result128 : result128;
}
_int128 _strtoi128_l(const char *str, char **end, int radix, _locale_t locale) {
return strtoint128<_int128 ,char ,true >(str, end, radix, locale);
}
_uint128 _strtoui128_l(const char *str, char **end, int radix, _locale_t locale) {
return strtoint128<_uint128,char ,false>(str, end, radix, locale);
}
_int128 _wcstoi128_l(const wchar_t *str, wchar_t **end, int radix, _locale_t locale) {
return strtoint128<_int128 ,wchar_t,true >(str, end, radix, locale);
}
_uint128 _wcstoui128_l(const wchar_t *str, wchar_t **end, int radix, _locale_t locale) {
return strtoint128<_uint128,wchar_t,false>(str, end, radix, locale);
}