171 lines
5.7 KiB
C
171 lines
5.7 KiB
C
/*
|
|
* 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
|