Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
352 changes: 352 additions & 0 deletions Modules/_decimal/libmpdec/mpdecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -8656,3 +8656,355 @@ mpd_qimport_u32(mpd_t *result,
mpd_qresize(result, result->len, status);
mpd_qfinalize(result, ctx, status);
}


/******************************************************************************/
/* From triple */
/******************************************************************************/

#if defined(CONFIG_64) && defined(__SIZEOF_INT128__)
static mpd_ssize_t
_set_coeff(uint64_t data[3], uint64_t hi, uint64_t lo)
{
__uint128_t d = ((__uint128_t)hi << 64) + lo;
__uint128_t q, r;

q = d / MPD_RADIX;
r = d % MPD_RADIX;
data[0] = (uint64_t)r;
d = q;

q = d / MPD_RADIX;
r = d % MPD_RADIX;
data[1] = (uint64_t)r;
d = q;

q = d / MPD_RADIX;
r = d % MPD_RADIX;
data[2] = (uint64_t)r;

if (q != 0) {
abort(); /* GCOV_NOT_REACHED */
}

return data[2] != 0 ? 3 : (data[1] != 0 ? 2 : 1);
}
#else
static size_t
_uint_from_u16(mpd_uint_t *w, mpd_ssize_t wlen, const uint16_t *u, size_t ulen)
{
const mpd_uint_t ubase = 1U<<16;
mpd_ssize_t n = 0;
mpd_uint_t carry;

assert(wlen > 0 && ulen > 0);

w[n++] = u[--ulen];
while (--ulen != SIZE_MAX) {
carry = _mpd_shortmul_c(w, w, n, ubase);
if (carry) {
if (n >= wlen) {
abort(); /* GCOV_NOT_REACHED */
}
w[n++] = carry;
}
carry = _mpd_shortadd(w, n, u[ulen]);
if (carry) {
if (n >= wlen) {
abort(); /* GCOV_NOT_REACHED */
}
w[n++] = carry;
}
}

return n;
}

static mpd_ssize_t
_set_coeff(mpd_uint_t *data, mpd_ssize_t len, uint64_t hi, uint64_t lo)
{
uint16_t u16[8] = {0};

u16[7] = (uint16_t)((hi & 0xFFFF000000000000ULL) >> 48);
u16[6] = (uint16_t)((hi & 0x0000FFFF00000000ULL) >> 32);
u16[5] = (uint16_t)((hi & 0x00000000FFFF0000ULL) >> 16);
u16[4] = (uint16_t) (hi & 0x000000000000FFFFULL);

u16[3] = (uint16_t)((lo & 0xFFFF000000000000ULL) >> 48);
u16[2] = (uint16_t)((lo & 0x0000FFFF00000000ULL) >> 32);
u16[1] = (uint16_t)((lo & 0x00000000FFFF0000ULL) >> 16);
u16[0] = (uint16_t) (lo & 0x000000000000FFFFULL);

return (mpd_ssize_t)_uint_from_u16(data, len, u16, 8);
}
#endif

static int
_set_uint128_coeff_exp(mpd_t *result, uint64_t hi, uint64_t lo, mpd_ssize_t exp)
{
mpd_uint_t data[5] = {0};
uint32_t status = 0;
mpd_ssize_t len;

#if defined(CONFIG_64) && defined(__SIZEOF_INT128__)
len = _set_coeff(data, hi, lo);
#else
len = _set_coeff(data, 5, hi, lo);
#endif

if (!mpd_qresize(result, len, &status)) {
return -1;
}

for (mpd_ssize_t i = 0; i < len; i++) {
result->data[i] = data[i];
}

result->exp = exp;
result->len = len;
mpd_setdigits(result);

return 0;
}

int
mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status)
{
static const mpd_context_t maxcontext = {
.prec=MPD_MAX_PREC,
.emax=MPD_MAX_EMAX,
.emin=MPD_MIN_EMIN,
.round=MPD_ROUND_HALF_EVEN,
.traps=MPD_Traps,
.status=0,
.newtrap=0,
.clamp=0,
.allcr=1,
};
const enum mpd_triple_class tag = triple->tag;
const uint8_t sign = triple->sign;
const uint64_t hi = triple->hi;
const uint64_t lo = triple->lo;
mpd_ssize_t exp;

#ifdef CONFIG_32
if (triple->exp < MPD_SSIZE_MIN || triple->exp > MPD_SSIZE_MAX) {
goto conversion_error;
}
#endif
exp = (mpd_ssize_t)triple->exp;

switch (tag) {
case MPD_TRIPLE_QNAN: case MPD_TRIPLE_SNAN: {
if (sign > 1 || exp != 0) {
goto conversion_error;
}

const uint8_t flags = tag == MPD_TRIPLE_QNAN ? MPD_NAN : MPD_SNAN;
mpd_setspecial(result, sign, flags);

if (hi == 0 && lo == 0) { /* no payload */
return 0;
}

if (_set_uint128_coeff_exp(result, hi, lo, exp) < 0) {
goto malloc_error;
}

return 0;
}

case MPD_TRIPLE_INF: {
if (sign > 1 || hi != 0 || lo != 0 || exp != 0) {
goto conversion_error;
}

mpd_setspecial(result, sign, MPD_INF);

return 0;
}

case MPD_TRIPLE_NORMAL: {
if (sign > 1) {
goto conversion_error;
}

const uint8_t flags = sign ? MPD_NEG : MPD_POS;
mpd_set_flags(result, flags);

if (exp > MPD_EXP_INF) {
exp = MPD_EXP_INF;
}
if (exp == MPD_SSIZE_MIN) {
exp = MPD_SSIZE_MIN+1;
}

if (_set_uint128_coeff_exp(result, hi, lo, exp) < 0) {
goto malloc_error;
}

uint32_t workstatus = 0;
mpd_qfinalize(result, &maxcontext, &workstatus);
if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
goto conversion_error;
}

return 0;
}

default:
goto conversion_error;
}

conversion_error:
mpd_seterror(result, MPD_Conversion_syntax, status);
return -1;

malloc_error:
mpd_seterror(result, MPD_Malloc_error, status);
return -1;
}


/******************************************************************************/
/* As triple */
/******************************************************************************/

#if defined(CONFIG_64) && defined(__SIZEOF_INT128__)
static void
_get_coeff(uint64_t *hi, uint64_t *lo, const mpd_t *a)
{
__uint128_t u128 = 0;

switch (a->len) {
case 3:
u128 = a->data[2]; /* fall through */
case 2:
u128 = u128 * MPD_RADIX + a->data[1]; /* fall through */
case 1:
u128 = u128 * MPD_RADIX + a->data[0];
break;
default:
abort(); /* GCOV_NOT_REACHED */
}

*hi = u128 >> 64;
*lo = (uint64_t)u128;
}
#else
static size_t
_uint_to_u16(uint16_t w[8], mpd_uint_t *u, mpd_ssize_t ulen)
{
const mpd_uint_t wbase = 1U<<16;
size_t n = 0;

assert(ulen > 0);

do {
if (n >= 8) {
abort(); /* GCOV_NOT_REACHED */
}
w[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase);
/* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */
ulen = _mpd_real_size(u, ulen);

} while (u[ulen-1] != 0);

return n;
}

static void
_get_coeff(uint64_t *hi, uint64_t *lo, const mpd_t *a)
{
uint16_t u16[8] = {0};
mpd_uint_t data[5] = {0};

switch (a->len) {
case 5:
data[4] = a->data[4]; /* fall through */
case 4:
data[3] = a->data[3]; /* fall through */
case 3:
data[2] = a->data[2]; /* fall through */
case 2:
data[1] = a->data[1]; /* fall through */
case 1:
data[0] = a->data[0];
break;
default:
abort(); /* GCOV_NOT_REACHED */
}

_uint_to_u16(u16, data, a->len);

*hi = (uint64_t)u16[7] << 48;
*hi |= (uint64_t)u16[6] << 32;
*hi |= (uint64_t)u16[5] << 16;
*hi |= (uint64_t)u16[4];

*lo = (uint64_t)u16[3] << 48;
*lo |= (uint64_t)u16[2] << 32;
*lo |= (uint64_t)u16[1] << 16;
*lo |= (uint64_t)u16[0];
}
#endif

static enum mpd_triple_class
_coeff_as_uint128(uint64_t *hi, uint64_t *lo, const mpd_t *a)
{
#ifdef CONFIG_64
static mpd_uint_t uint128_max_data[3] = { 3374607431768211455ULL, 4028236692093846346ULL, 3ULL };
static const mpd_t uint128_max = { MPD_STATIC|MPD_CONST_DATA, 0, 39, 3, 3, uint128_max_data };
#else
static mpd_uint_t uint128_max_data[5] = { 768211455U, 374607431U, 938463463U, 282366920U, 340U };
static const mpd_t uint128_max = { MPD_STATIC|MPD_CONST_DATA, 0, 39, 5, 5, uint128_max_data };
#endif
enum mpd_triple_class ret = MPD_TRIPLE_NORMAL;
uint32_t status = 0;
mpd_t coeff;

*hi = *lo = 0ULL;

if (mpd_isspecial(a)) {
if (mpd_isinfinite(a)) {
return MPD_TRIPLE_INF;
}

ret = mpd_isqnan(a) ? MPD_TRIPLE_QNAN : MPD_TRIPLE_SNAN;
if (a->len == 0) { /* no payload */
return ret;
}
}
else if (mpd_iszero(a)) {
return ret;
}

_mpd_copy_shared(&coeff, a);
mpd_set_flags(&coeff, 0);
coeff.exp = 0;

if (mpd_qcmp(&coeff, &uint128_max, &status) > 0) {
return MPD_TRIPLE_ERROR;
}

_get_coeff(hi, lo, &coeff);
return ret;
}

mpd_uint128_triple_t
mpd_as_uint128_triple(const mpd_t *a)
{
mpd_uint128_triple_t triple = { MPD_TRIPLE_ERROR, 0, 0, 0, 0 };

triple.tag = _coeff_as_uint128(&triple.hi, &triple.lo, a);
if (triple.tag == MPD_TRIPLE_ERROR) {
return triple;
}

triple.sign = !!mpd_isnegative(a);
if (triple.tag == MPD_TRIPLE_NORMAL) {
triple.exp = a->exp;
}

return triple;
}
25 changes: 25 additions & 0 deletions Modules/_decimal/libmpdec/mpdecimal.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,31 @@ typedef struct mpd_t {
typedef unsigned char uchar;


/******************************************************************************/
/* Triple */
/******************************************************************************/

/* status cases for getting a triple */
enum mpd_triple_class {
MPD_TRIPLE_NORMAL,
MPD_TRIPLE_INF,
MPD_TRIPLE_QNAN,
MPD_TRIPLE_SNAN,
MPD_TRIPLE_ERROR,
};

typedef struct {
enum mpd_triple_class tag;
uint8_t sign;
uint64_t hi;
uint64_t lo;
int64_t exp;
} mpd_uint128_triple_t;

int mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status);
mpd_uint128_triple_t mpd_as_uint128_triple(const mpd_t *a);


/******************************************************************************/
/* Quiet, thread-safe functions */
/******************************************************************************/
Expand Down