mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
![Mikael Pettersson](/assets/img/avatar_default.png)
The kernel's math-emu code contains a macro _FP_FROM_INT() which is used to convert an integer to a raw normalized floating-point value. It does this basically in three steps: 1. Compute the exponent from the number of leading zero bits. 2. Downshift large fractions to put the MSB in the right position for normalized fractions. 3. Upshift small fractions to put the MSB in the right position. There is an boundary error in step 2, causing a fraction with its MSB exactly one bit above the normalized MSB position to not be downshifted. This results in a non-normalized raw float, which when packed becomes a massively inaccurate representation for that input. The impact of this depends on a number of arch-specific factors, but it is known to have broken emulation of FXTOD instructions on UltraSPARC III, which was originally reported as GCC bug 44631 <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44631>. Any arch which uses math-emu to emulate conversions from integers to same-size floats may be affected. The fix is simple: the exponent comparison used to determine if the fraction should be downshifted must be "<=" not "<". I'm sending a kernel module to test this as a reply to this message. There are also SPARC user-space test cases in the GCC bug entry. Signed-off-by: Mikael Pettersson <mikpe@it.uu.se> Signed-off-by: David S. Miller <davem@davemloft.net>
871 lines
26 KiB
C
871 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 truly 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); \
|
|
if (_FP_FRAC_HIGH_##fs(X) \
|
|
& (_FP_OVERFLOW_##fs >> 1)) \
|
|
{ \
|
|
X##_e = 1; \
|
|
_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \
|
|
} \
|
|
else \
|
|
{ \
|
|
_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);\
|
|
break; \
|
|
\
|
|
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__ */
|