blob: 9360ac1c904eebc728a02bccd0d33a20c3c47faa [file] [log] [blame] [raw]
/*
* SoftFP Library
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if ICVT_SIZE == 32
#define ICVT_UINT uint32_t
#define ICVT_INT int32_t
#elif ICVT_SIZE == 64
#define ICVT_UINT uint64_t
#define ICVT_INT int64_t
#elif ICVT_SIZE == 128
#define ICVT_UINT uint128_t
#define ICVT_INT int128_t
#else
#error unsupported icvt
#endif
/* conversions between float and integers */
static ICVT_INT glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
uint32_t *pfflags, BOOL is_unsigned)
{
uint32_t a_sign, addend, rnd_bits;
int32_t a_exp;
F_UINT a_mant;
ICVT_UINT r, r_max;
a_sign = a >> (F_SIZE - 1);
a_exp = (a >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
if (a_exp == EXP_MASK && a_mant != 0)
a_sign = 0; /* NaN is like +infinity */
if (a_exp == 0) {
a_exp = 1;
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
a_mant <<= RND_SIZE;
a_exp = a_exp - (EXP_MASK / 2) - MANT_SIZE;
if (is_unsigned)
r_max = (ICVT_UINT)a_sign - 1;
else
r_max = ((ICVT_UINT)1 << (ICVT_SIZE - 1)) - (ICVT_UINT)(a_sign ^ 1);
if (a_exp >= 0) {
if (a_exp <= (ICVT_SIZE - 1 - MANT_SIZE)) {
r = (ICVT_UINT)(a_mant >> RND_SIZE) << a_exp;
if (r > r_max)
goto overflow;
} else {
overflow:
*pfflags |= FFLAG_INVALID_OP;
return r_max;
}
} else {
a_mant = rshift_rnd(a_mant, -a_exp);
switch(rm) {
case RM_RNE:
case RM_RMM:
addend = (1 << (RND_SIZE - 1));
break;
case RM_RTZ:
addend = 0;
break;
default:
case RM_RDN:
case RM_RUP:
if (a_sign ^ (rm & 1))
addend = (1 << RND_SIZE) - 1;
else
addend = 0;
break;
}
rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
a_mant = (a_mant + addend) >> RND_SIZE;
/* half way: select even result */
if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))
a_mant &= ~1;
if (a_mant > r_max)
goto overflow;
r = a_mant;
if (rnd_bits != 0)
*pfflags |= FFLAG_INEXACT;
}
if (a_sign)
r = -r;
return r;
}
ICVT_INT glue(glue(glue(cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(a, rm,
pfflags, FALSE);
}
ICVT_UINT glue(glue(glue(cvt_sf, F_SIZE), _u), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE) (a, rm,
pfflags, TRUE);
}
/* conversions between float and integers */
static F_UINT glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
RoundingModeEnum rm,
uint32_t *pfflags,
BOOL is_unsigned)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
ICVT_UINT r, mask;
int l;
if (!is_unsigned && a < 0) {
a_sign = 1;
r = -(ICVT_UINT)a;
} else {
a_sign = 0;
r = a;
}
a_exp = (EXP_MASK / 2) + F_SIZE - 2;
/* need to reduce range before generic float normalization */
l = ICVT_SIZE - glue(clz, ICVT_SIZE)(r) - (F_SIZE - 1);
if (l > 0) {
mask = r & (((ICVT_UINT)1 << l) - 1);
r = (r >> l) | ((r & mask) != 0);
a_exp += l;
}
a_mant = r;
return normalize_sf(a_sign, a_exp, a_mant, rm, pfflags);
}
F_UINT glue(glue(glue(cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, pfflags, FALSE);
}
F_UINT glue(glue(glue(cvt_u, ICVT_SIZE), _sf), F_SIZE)(ICVT_UINT a,
RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, pfflags, TRUE);
}
#undef ICVT_SIZE
#undef ICVT_INT
#undef ICVT_UINT