aha/include/math-emu/op-common.h
Kumar Gala 48d6c64311 math-emu: Add support for reporting exact invalid exception
Some architectures (like powerpc) provide status information on the exact
type of invalid exception.  This is pretty straight forward as we already
report invalid exceptions via FP_SET_EXCEPTION.

We add new flags (FP_EX_INVALID_*) the architecture code can define if it
wants the exact invalid exception reported.

We had to split out the INF/INF and 0/0 cases for divide to allow reporting
the two invalid forms properly.

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Acked-by: David S. Miller <davem@davemloft.net>
2008-09-16 10:01:37 -05:00

860 lines
26 KiB
C

/* Software floating-point emulation. Common operations.
Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson (rth@cygnus.com),
Jakub Jelinek (jj@ultra.linux.cz),
David S. Miller (davem@redhat.com) and
Peter Maydell (pmaydell@chiark.greenend.org.uk).
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifndef __MATH_EMU_OP_COMMON_H__
#define __MATH_EMU_OP_COMMON_H__
#define _FP_DECL(wc, X) \
_FP_I_TYPE X##_c=0, X##_s=0, X##_e=0; \
_FP_FRAC_DECL_##wc(X)
/*
* Finish truely unpacking a native fp value by classifying the kind
* of fp value and normalizing both the exponent and the fraction.
*/
#define _FP_UNPACK_CANONICAL(fs, wc, X) \
do { \
switch (X##_e) \
{ \
default: \
_FP_FRAC_HIGH_RAW_##fs(X) |= _FP_IMPLBIT_##fs; \
_FP_FRAC_SLL_##wc(X, _FP_WORKBITS); \
X##_e -= _FP_EXPBIAS_##fs; \
X##_c = FP_CLS_NORMAL; \
break; \
\
case 0: \
if (_FP_FRAC_ZEROP_##wc(X)) \
X##_c = FP_CLS_ZERO; \
else \
{ \
/* a denormalized number */ \
_FP_I_TYPE _shift; \
_FP_FRAC_CLZ_##wc(_shift, X); \
_shift -= _FP_FRACXBITS_##fs; \
_FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \
X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \
X##_c = FP_CLS_NORMAL; \
FP_SET_EXCEPTION(FP_EX_DENORM); \
if (FP_DENORM_ZERO) \
{ \
FP_SET_EXCEPTION(FP_EX_INEXACT); \
X##_c = FP_CLS_ZERO; \
} \
} \
break; \
\
case _FP_EXPMAX_##fs: \
if (_FP_FRAC_ZEROP_##wc(X)) \
X##_c = FP_CLS_INF; \
else \
{ \
X##_c = FP_CLS_NAN; \
/* Check for signaling NaN */ \
if (!(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs)) \
FP_SET_EXCEPTION(FP_EX_INVALID | FP_EX_INVALID_SNAN); \
} \
break; \
} \
} while (0)
/*
* Before packing the bits back into the native fp result, take care
* of such mundane things as rounding and overflow. Also, for some
* kinds of fp values, the original parts may not have been fully
* extracted -- but that is ok, we can regenerate them now.
*/
#define _FP_PACK_CANONICAL(fs, wc, X) \
do { \
switch (X##_c) \
{ \
case FP_CLS_NORMAL: \
X##_e += _FP_EXPBIAS_##fs; \
if (X##_e > 0) \
{ \
_FP_ROUND(wc, X); \
if (_FP_FRAC_OVERP_##wc(fs, X)) \
{ \
_FP_FRAC_CLEAR_OVERP_##wc(fs, X); \
X##_e++; \
} \
_FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \
if (X##_e >= _FP_EXPMAX_##fs) \
{ \
/* overflow */ \
switch (FP_ROUNDMODE) \
{ \
case FP_RND_NEAREST: \
X##_c = FP_CLS_INF; \
break; \
case FP_RND_PINF: \
if (!X##_s) X##_c = FP_CLS_INF; \
break; \
case FP_RND_MINF: \
if (X##_s) X##_c = FP_CLS_INF; \
break; \
} \
if (X##_c == FP_CLS_INF) \
{ \
/* Overflow to infinity */ \
X##_e = _FP_EXPMAX_##fs; \
_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \
} \
else \
{ \
/* Overflow to maximum normal */ \
X##_e = _FP_EXPMAX_##fs - 1; \
_FP_FRAC_SET_##wc(X, _FP_MAXFRAC_##wc); \
} \
FP_SET_EXCEPTION(FP_EX_OVERFLOW); \
FP_SET_EXCEPTION(FP_EX_INEXACT); \
} \
} \
else \
{ \
/* we've got a denormalized number */ \
X##_e = -X##_e + 1; \
if (X##_e <= _FP_WFRACBITS_##fs) \
{ \
_FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \
_FP_ROUND(wc, X); \
if (_FP_FRAC_HIGH_##fs(X) \
& (_FP_OVERFLOW_##fs >> 1)) \
{ \
X##_e = 1; \
_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \
FP_SET_EXCEPTION(FP_EX_INEXACT); \
} \
else \
{ \
X##_e = 0; \
_FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \
} \
if ((FP_CUR_EXCEPTIONS & FP_EX_INEXACT) || \
(FP_TRAPPING_EXCEPTIONS & FP_EX_UNDERFLOW)) \
FP_SET_EXCEPTION(FP_EX_UNDERFLOW); \
} \
else \
{ \
/* underflow to zero */ \
X##_e = 0; \
if (!_FP_FRAC_ZEROP_##wc(X)) \
{ \
_FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \
_FP_ROUND(wc, X); \
_FP_FRAC_LOW_##wc(X) >>= (_FP_WORKBITS); \
} \
FP_SET_EXCEPTION(FP_EX_UNDERFLOW); \
} \
} \
break; \
\
case FP_CLS_ZERO: \
X##_e = 0; \
_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \
break; \
\
case FP_CLS_INF: \
X##_e = _FP_EXPMAX_##fs; \
_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \
break; \
\
case FP_CLS_NAN: \
X##_e = _FP_EXPMAX_##fs; \
if (!_FP_KEEPNANFRACP) \
{ \
_FP_FRAC_SET_##wc(X, _FP_NANFRAC_##fs); \
X##_s = _FP_NANSIGN_##fs; \
} \
else \
_FP_FRAC_HIGH_RAW_##fs(X) |= _FP_QNANBIT_##fs; \
break; \
} \
} while (0)
/* This one accepts raw argument and not cooked, returns
* 1 if X is a signaling NaN.
*/
#define _FP_ISSIGNAN(fs, wc, X) \
({ \
int __ret = 0; \
if (X##_e == _FP_EXPMAX_##fs) \
{ \
if (!_FP_FRAC_ZEROP_##wc(X) \
&& !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs)) \
__ret = 1; \
} \
__ret; \
})
/*
* Main addition routine. The input values should be cooked.
*/
#define _FP_ADD_INTERNAL(fs, wc, R, X, Y, OP) \
do { \
switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \
{ \
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \
{ \
/* shift the smaller number so that its exponent matches the larger */ \
_FP_I_TYPE diff = X##_e - Y##_e; \
\
if (diff < 0) \
{ \
diff = -diff; \
if (diff <= _FP_WFRACBITS_##fs) \
_FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \
else if (!_FP_FRAC_ZEROP_##wc(X)) \
_FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \
R##_e = Y##_e; \
} \
else \
{ \
if (diff > 0) \
{ \
if (diff <= _FP_WFRACBITS_##fs) \
_FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \
else if (!_FP_FRAC_ZEROP_##wc(Y)) \
_FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \
} \
R##_e = X##_e; \
} \
\
R##_c = FP_CLS_NORMAL; \
\
if (X##_s == Y##_s) \
{ \
R##_s = X##_s; \
_FP_FRAC_ADD_##wc(R, X, Y); \
if (_FP_FRAC_OVERP_##wc(fs, R)) \
{ \
_FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \
R##_e++; \
} \
} \
else \
{ \
R##_s = X##_s; \
_FP_FRAC_SUB_##wc(R, X, Y); \
if (_FP_FRAC_ZEROP_##wc(R)) \
{ \
/* return an exact zero */ \
if (FP_ROUNDMODE == FP_RND_MINF) \
R##_s |= Y##_s; \
else \
R##_s &= Y##_s; \
R##_c = FP_CLS_ZERO; \
} \
else \
{ \
if (_FP_FRAC_NEGP_##wc(R)) \
{ \
_FP_FRAC_SUB_##wc(R, Y, X); \
R##_s = Y##_s; \
} \
\
/* renormalize after subtraction */ \
_FP_FRAC_CLZ_##wc(diff, R); \
diff -= _FP_WFRACXBITS_##fs; \
if (diff) \
{ \
R##_e -= diff; \
_FP_FRAC_SLL_##wc(R, diff); \
} \
} \
} \
break; \
} \
\
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \
_FP_CHOOSENAN(fs, wc, R, X, Y, OP); \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \
R##_e = X##_e; \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \
_FP_FRAC_COPY_##wc(R, X); \
R##_s = X##_s; \
R##_c = X##_c; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \
R##_e = Y##_e; \
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \
_FP_FRAC_COPY_##wc(R, Y); \
R##_s = Y##_s; \
R##_c = Y##_c; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \
if (X##_s != Y##_s) \
{ \
/* +INF + -INF => NAN */ \
_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \
R##_s = _FP_NANSIGN_##fs; \
R##_c = FP_CLS_NAN; \
FP_SET_EXCEPTION(FP_EX_INVALID | FP_EX_INVALID_ISI); \
break; \
} \
/* FALLTHRU */ \
\
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \
R##_s = X##_s; \
R##_c = FP_CLS_INF; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \
R##_s = Y##_s; \
R##_c = FP_CLS_INF; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \
/* make sure the sign is correct */ \
if (FP_ROUNDMODE == FP_RND_MINF) \
R##_s = X##_s | Y##_s; \
else \
R##_s = X##_s & Y##_s; \
R##_c = FP_CLS_ZERO; \
break; \
\
default: \
abort(); \
} \
} while (0)
#define _FP_ADD(fs, wc, R, X, Y) _FP_ADD_INTERNAL(fs, wc, R, X, Y, '+')
#define _FP_SUB(fs, wc, R, X, Y) \
do { \
if (Y##_c != FP_CLS_NAN) Y##_s ^= 1; \
_FP_ADD_INTERNAL(fs, wc, R, X, Y, '-'); \
} while (0)
/*
* Main negation routine. FIXME -- when we care about setting exception
* bits reliably, this will not do. We should examine all of the fp classes.
*/
#define _FP_NEG(fs, wc, R, X) \
do { \
_FP_FRAC_COPY_##wc(R, X); \
R##_c = X##_c; \
R##_e = X##_e; \
R##_s = 1 ^ X##_s; \
} while (0)
/*
* Main multiplication routine. The input values should be cooked.
*/
#define _FP_MUL(fs, wc, R, X, Y) \
do { \
R##_s = X##_s ^ Y##_s; \
switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \
{ \
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \
R##_c = FP_CLS_NORMAL; \
R##_e = X##_e + Y##_e + 1; \
\
_FP_MUL_MEAT_##fs(R,X,Y); \
\
if (_FP_FRAC_OVERP_##wc(fs, R)) \
_FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \
else \
R##_e--; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \
_FP_CHOOSENAN(fs, wc, R, X, Y, '*'); \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \
R##_s = X##_s; \
\
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \
_FP_FRAC_COPY_##wc(R, X); \
R##_c = X##_c; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \
R##_s = Y##_s; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \
_FP_FRAC_COPY_##wc(R, Y); \
R##_c = Y##_c; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \
R##_s = _FP_NANSIGN_##fs; \
R##_c = FP_CLS_NAN; \
_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \
FP_SET_EXCEPTION(FP_EX_INVALID | FP_EX_INVALID_IMZ);\
break; \
\
default: \
abort(); \
} \
} while (0)
/*
* Main division routine. The input values should be cooked.
*/
#define _FP_DIV(fs, wc, R, X, Y) \
do { \
R##_s = X##_s ^ Y##_s; \
switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \
{ \
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \
R##_c = FP_CLS_NORMAL; \
R##_e = X##_e - Y##_e; \
\
_FP_DIV_MEAT_##fs(R,X,Y); \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \
_FP_CHOOSENAN(fs, wc, R, X, Y, '/'); \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \
R##_s = X##_s; \
_FP_FRAC_COPY_##wc(R, X); \
R##_c = X##_c; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \
R##_s = Y##_s; \
_FP_FRAC_COPY_##wc(R, Y); \
R##_c = Y##_c; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \
R##_c = FP_CLS_ZERO; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \
FP_SET_EXCEPTION(FP_EX_DIVZERO); \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \
R##_c = FP_CLS_INF; \
break; \
\
case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \
R##_s = _FP_NANSIGN_##fs; \
R##_c = FP_CLS_NAN; \
_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \
FP_SET_EXCEPTION(FP_EX_INVALID | FP_EX_INVALID_IDI);\
case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \
R##_s = _FP_NANSIGN_##fs; \
R##_c = FP_CLS_NAN; \
_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \
FP_SET_EXCEPTION(FP_EX_INVALID | FP_EX_INVALID_ZDZ);\
break; \
\
default: \
abort(); \
} \
} while (0)
/*
* Main differential comparison routine. The inputs should be raw not
* cooked. The return is -1,0,1 for normal values, 2 otherwise.
*/
#define _FP_CMP(fs, wc, ret, X, Y, un) \
do { \
/* NANs are unordered */ \
if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \
|| (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \
{ \
ret = un; \
} \
else \
{ \
int __is_zero_x; \
int __is_zero_y; \
\
__is_zero_x = (!X##_e && _FP_FRAC_ZEROP_##wc(X)) ? 1 : 0; \
__is_zero_y = (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) ? 1 : 0; \
\
if (__is_zero_x && __is_zero_y) \
ret = 0; \
else if (__is_zero_x) \
ret = Y##_s ? 1 : -1; \
else if (__is_zero_y) \
ret = X##_s ? -1 : 1; \
else if (X##_s != Y##_s) \
ret = X##_s ? -1 : 1; \
else if (X##_e > Y##_e) \
ret = X##_s ? -1 : 1; \
else if (X##_e < Y##_e) \
ret = X##_s ? 1 : -1; \
else if (_FP_FRAC_GT_##wc(X, Y)) \
ret = X##_s ? -1 : 1; \
else if (_FP_FRAC_GT_##wc(Y, X)) \
ret = X##_s ? 1 : -1; \
else \
ret = 0; \
} \
} while (0)
/* Simplification for strict equality. */
#define _FP_CMP_EQ(fs, wc, ret, X, Y) \
do { \
/* NANs are unordered */ \
if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \
|| (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \
{ \
ret = 1; \
} \
else \
{ \
ret = !(X##_e == Y##_e \
&& _FP_FRAC_EQ_##wc(X, Y) \
&& (X##_s == Y##_s || !X##_e && _FP_FRAC_ZEROP_##wc(X))); \
} \
} while (0)
/*
* Main square root routine. The input value should be cooked.
*/
#define _FP_SQRT(fs, wc, R, X) \
do { \
_FP_FRAC_DECL_##wc(T); _FP_FRAC_DECL_##wc(S); \
_FP_W_TYPE q; \
switch (X##_c) \
{ \
case FP_CLS_NAN: \
_FP_FRAC_COPY_##wc(R, X); \
R##_s = X##_s; \
R##_c = FP_CLS_NAN; \
break; \
case FP_CLS_INF: \
if (X##_s) \
{ \
R##_s = _FP_NANSIGN_##fs; \
R##_c = FP_CLS_NAN; /* NAN */ \
_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \
FP_SET_EXCEPTION(FP_EX_INVALID); \
} \
else \
{ \
R##_s = 0; \
R##_c = FP_CLS_INF; /* sqrt(+inf) = +inf */ \
} \
break; \
case FP_CLS_ZERO: \
R##_s = X##_s; \
R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */ \
break; \
case FP_CLS_NORMAL: \
R##_s = 0; \
if (X##_s) \
{ \
R##_c = FP_CLS_NAN; /* sNAN */ \
R##_s = _FP_NANSIGN_##fs; \
_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \
FP_SET_EXCEPTION(FP_EX_INVALID); \
break; \
} \
R##_c = FP_CLS_NORMAL; \
if (X##_e & 1) \
_FP_FRAC_SLL_##wc(X, 1); \
R##_e = X##_e >> 1; \
_FP_FRAC_SET_##wc(S, _FP_ZEROFRAC_##wc); \
_FP_FRAC_SET_##wc(R, _FP_ZEROFRAC_##wc); \
q = _FP_OVERFLOW_##fs >> 1; \
_FP_SQRT_MEAT_##wc(R, S, T, X, q); \
} \
} while (0)
/*
* Convert from FP to integer
*/
/* RSIGNED can have following values:
* 0: the number is required to be 0..(2^rsize)-1, if not, NV is set plus
* the result is either 0 or (2^rsize)-1 depending on the sign in such case.
* 1: the number is required to be -(2^(rsize-1))..(2^(rsize-1))-1, if not, NV is
* set plus the result is either -(2^(rsize-1)) or (2^(rsize-1))-1 depending
* on the sign in such case.
* 2: the number is required to be -(2^(rsize-1))..(2^(rsize-1))-1, if not, NV is
* set plus the result is truncated to fit into destination.
* -1: the number is required to be -(2^(rsize-1))..(2^rsize)-1, if not, NV is
* set plus the result is either -(2^(rsize-1)) or (2^(rsize-1))-1 depending
* on the sign in such case.
*/
#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \
do { \
switch (X##_c) \
{ \
case FP_CLS_NORMAL: \
if (X##_e < 0) \
{ \
FP_SET_EXCEPTION(FP_EX_INEXACT); \
case FP_CLS_ZERO: \
r = 0; \
} \
else if (X##_e >= rsize - (rsigned > 0 || X##_s) \
|| (!rsigned && X##_s)) \
{ /* overflow */ \
case FP_CLS_NAN: \
case FP_CLS_INF: \
if (rsigned == 2) \
{ \
if (X##_c != FP_CLS_NORMAL \
|| X##_e >= rsize - 1 + _FP_WFRACBITS_##fs) \
r = 0; \
else \
{ \
_FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1)); \
_FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \
} \
} \
else if (rsigned) \
{ \
r = 1; \
r <<= rsize - 1; \
r -= 1 - X##_s; \
} \
else \
{ \
r = 0; \
if (X##_s) \
r = ~r; \
} \
FP_SET_EXCEPTION(FP_EX_INVALID); \
} \
else \
{ \
if (_FP_W_TYPE_SIZE*wc < rsize) \
{ \
_FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \
r <<= X##_e - _FP_WFRACBITS_##fs; \
} \
else \
{ \
if (X##_e >= _FP_WFRACBITS_##fs) \
_FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1)); \
else if (X##_e < _FP_WFRACBITS_##fs - 1) \
{ \
_FP_FRAC_SRS_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 2), \
_FP_WFRACBITS_##fs); \
if (_FP_FRAC_LOW_##wc(X) & 1) \
FP_SET_EXCEPTION(FP_EX_INEXACT); \
_FP_FRAC_SRL_##wc(X, 1); \
} \
_FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \
} \
if (rsigned && X##_s) \
r = -r; \
} \
break; \
} \
} while (0)
#define _FP_TO_INT_ROUND(fs, wc, r, X, rsize, rsigned) \
do { \
r = 0; \
switch (X##_c) \
{ \
case FP_CLS_NORMAL: \
if (X##_e >= _FP_FRACBITS_##fs - 1) \
{ \
if (X##_e < rsize - 1 + _FP_WFRACBITS_##fs) \
{ \
if (X##_e >= _FP_WFRACBITS_##fs - 1) \
{ \
_FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \
r <<= X##_e - _FP_WFRACBITS_##fs + 1; \
} \
else \
{ \
_FP_FRAC_SRL_##wc(X, _FP_WORKBITS - X##_e \
+ _FP_FRACBITS_##fs - 1); \
_FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \
} \
} \
} \
else \
{ \
if (X##_e <= -_FP_WORKBITS - 1) \
_FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \
else \
_FP_FRAC_SRS_##wc(X, _FP_FRACBITS_##fs - 1 - X##_e, \
_FP_WFRACBITS_##fs); \
_FP_ROUND(wc, X); \
_FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \
_FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \
} \
if (rsigned && X##_s) \
r = -r; \
if (X##_e >= rsize - (rsigned > 0 || X##_s) \
|| (!rsigned && X##_s)) \
{ /* overflow */ \
case FP_CLS_NAN: \
case FP_CLS_INF: \
if (!rsigned) \
{ \
r = 0; \
if (X##_s) \
r = ~r; \
} \
else if (rsigned != 2) \
{ \
r = 1; \
r <<= rsize - 1; \
r -= 1 - X##_s; \
} \
FP_SET_EXCEPTION(FP_EX_INVALID); \
} \
break; \
case FP_CLS_ZERO: \
break; \
} \
} while (0)
#define _FP_FROM_INT(fs, wc, X, r, rsize, rtype) \
do { \
if (r) \
{ \
unsigned rtype ur_; \
X##_c = FP_CLS_NORMAL; \
\
if ((X##_s = (r < 0))) \
ur_ = (unsigned rtype) -r; \
else \
ur_ = (unsigned rtype) r; \
if (rsize <= _FP_W_TYPE_SIZE) \
__FP_CLZ(X##_e, ur_); \
else \
__FP_CLZ_2(X##_e, (_FP_W_TYPE)(ur_ >> _FP_W_TYPE_SIZE), \
(_FP_W_TYPE)ur_); \
if (rsize < _FP_W_TYPE_SIZE) \
X##_e -= (_FP_W_TYPE_SIZE - rsize); \
X##_e = rsize - X##_e - 1; \
\
if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e) \
__FP_FRAC_SRS_1(ur_, (X##_e - _FP_WFRACBITS_##fs + 1), rsize);\
_FP_FRAC_DISASSEMBLE_##wc(X, ur_, rsize); \
if ((_FP_WFRACBITS_##fs - X##_e - 1) > 0) \
_FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \
} \
else \
{ \
X##_c = FP_CLS_ZERO, X##_s = 0; \
} \
} while (0)
#define FP_CONV(dfs,sfs,dwc,swc,D,S) \
do { \
_FP_FRAC_CONV_##dwc##_##swc(dfs, sfs, D, S); \
D##_e = S##_e; \
D##_c = S##_c; \
D##_s = S##_s; \
} while (0)
/*
* Helper primitives.
*/
/* Count leading zeros in a word. */
#ifndef __FP_CLZ
#if _FP_W_TYPE_SIZE < 64
/* this is just to shut the compiler up about shifts > word length -- PMM 02/1998 */
#define __FP_CLZ(r, x) \
do { \
_FP_W_TYPE _t = (x); \
r = _FP_W_TYPE_SIZE - 1; \
if (_t > 0xffff) r -= 16; \
if (_t > 0xffff) _t >>= 16; \
if (_t > 0xff) r -= 8; \
if (_t > 0xff) _t >>= 8; \
if (_t & 0xf0) r -= 4; \
if (_t & 0xf0) _t >>= 4; \
if (_t & 0xc) r -= 2; \
if (_t & 0xc) _t >>= 2; \
if (_t & 0x2) r -= 1; \
} while (0)
#else /* not _FP_W_TYPE_SIZE < 64 */
#define __FP_CLZ(r, x) \
do { \
_FP_W_TYPE _t = (x); \
r = _FP_W_TYPE_SIZE - 1; \
if (_t > 0xffffffff) r -= 32; \
if (_t > 0xffffffff) _t >>= 32; \
if (_t > 0xffff) r -= 16; \
if (_t > 0xffff) _t >>= 16; \
if (_t > 0xff) r -= 8; \
if (_t > 0xff) _t >>= 8; \
if (_t & 0xf0) r -= 4; \
if (_t & 0xf0) _t >>= 4; \
if (_t & 0xc) r -= 2; \
if (_t & 0xc) _t >>= 2; \
if (_t & 0x2) r -= 1; \
} while (0)
#endif /* not _FP_W_TYPE_SIZE < 64 */
#endif /* ndef __FP_CLZ */
#define _FP_DIV_HELP_imm(q, r, n, d) \
do { \
q = n / d, r = n % d; \
} while (0)
#endif /* __MATH_EMU_OP_COMMON_H__ */