475 lines
8.9 KiB
C
475 lines
8.9 KiB
C
|
|
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ieeemisc.c
|
|
|
|
Abstract:
|
|
|
|
IEEE miscellaneous recommended functions
|
|
|
|
Author:
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
29-sept-1999 ATM Shafiqul Khalid [askhalid] copied from rtl library.
|
|
--*/
|
|
|
|
|
|
|
|
#include <trans.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
|
|
/***
|
|
* _copysign - copy sign
|
|
*
|
|
*Purpose:
|
|
* copysign(x,y) returns x with the sign of y. Hence, abs(x) := copysign
|
|
* even if x is NaN [IEEE std 854-1987 Appendix]
|
|
*
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
* No exceptions, even if one of the arguments is NaN.
|
|
*
|
|
* (Currently the i386 compiler returns doubles on the fp stack
|
|
* so the fld instruction at the end will cause an invalid operation
|
|
* if x is NaN. However this compiler calling convention will change
|
|
* soon)
|
|
*
|
|
*******************************************************************************/
|
|
|
|
double _copysign (double x, double y)
|
|
{
|
|
double retval;
|
|
*D_LO(retval) = *D_LO(x);
|
|
*D_HI(retval) = *D_HI(x) & ~(1<<31) |
|
|
*D_HI(y) & (1<<31) ;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
* _chgsign - change sign
|
|
*
|
|
*Purpose:
|
|
* x is copied with its sign reversed, not 0-x; the distinction is germane
|
|
* when x is +0, -0, or NaN
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
* No exceptions, even if x is NaN.
|
|
*
|
|
* (Currently the i386 compiler returns doubles on the fp stack
|
|
* so the fld instruction at the end will cause an invalid operation
|
|
* if x is NaN. However this compiler calling convention will change
|
|
* soon)
|
|
*
|
|
*******************************************************************************/
|
|
|
|
double _chgsign (double x)
|
|
{
|
|
double retval;
|
|
|
|
*D_LO(retval) = *D_LO(x);
|
|
*D_HI(retval) = *D_HI(x) & ~(1 << 31) |
|
|
~*D_HI(x) & (1<<31);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/***
|
|
* _scalb - scale by power of 2
|
|
*
|
|
*Purpose:
|
|
* _scalb(x,n) returns x * 2^n for integral values of n without
|
|
* computing 2^n
|
|
* Special case:
|
|
* If x is infinity or zero, _scaleb returns x
|
|
*
|
|
*
|
|
*Entry:
|
|
* double x
|
|
* int n
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
* Invalid operation, Overflow, Underflow
|
|
*
|
|
*******************************************************************************/
|
|
|
|
double _scalb(double x, long n)
|
|
{
|
|
//
|
|
// It turns out that our implementation of ldexp matces the IEEE
|
|
// description of _scalb. The only problem with calling ldexp
|
|
// is that if an exception occurs, the operation code reported
|
|
// to the handler will be the one that corresponds to ldexp
|
|
// (i.e., we do not define a new operation code for _scalb
|
|
//
|
|
|
|
return ldexp(x,n);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***
|
|
* _logb - extract exponent
|
|
*
|
|
*Purpose:
|
|
* _logb(x) returns the unbiased exponent of x, a signed integer in the
|
|
* format of x, except that logb(NaN) is a NaN, logb(+INF) is +INF,and
|
|
* logb(0) is is -INF and signals the division by zero exception.
|
|
* For x positive and finite, 1<= abs(scalb(x, -logb(x))) < 2
|
|
*
|
|
*
|
|
*Entry:
|
|
* double x
|
|
* int n
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
* Invalid operation, Division by zero
|
|
*
|
|
*******************************************************************************/
|
|
double _logb(double x)
|
|
{
|
|
unsigned int savedcw;
|
|
int exp;
|
|
double retval;
|
|
|
|
/* save user fp control word */
|
|
savedcw = _maskfp();
|
|
|
|
/* check for infinity or NAN */
|
|
if (IS_D_SPECIAL(x)){
|
|
switch (_sptype(x)) {
|
|
case T_PINF:
|
|
case T_NINF:
|
|
RETURN(savedcw, x);
|
|
case T_QNAN:
|
|
return _handle_qnan1(OP_LOGB, x, savedcw);
|
|
default: //T_SNAN
|
|
return _except1(FP_I, OP_LOGB, x, _s2qnan(x), savedcw);
|
|
}
|
|
}
|
|
|
|
if (x == 0) {
|
|
return _except1(FP_Z, OP_LOGB, x, -D_INF, savedcw);
|
|
}
|
|
|
|
(void) _decomp(x, &exp);
|
|
|
|
//
|
|
// x == man * 2^exp, where .5 <= man < 1. According to the spec
|
|
// of this function, we should compute the exponent so that
|
|
// 1<=man<2, i.e., we should decrement the computed exp by one
|
|
//
|
|
|
|
retval = (double) (exp - 1);
|
|
|
|
RETURN(savedcw, retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***
|
|
* _nextafter - next representable neighbor
|
|
*
|
|
*Purpose:
|
|
* _nextafter(x,y) returns the next representable neighbor of x in
|
|
* the direction toward y. The following special cases arise: if
|
|
* x=y, then the result is x without any exception being signaled;
|
|
* otherwise, if either x or y is a quiet NaN, then the result is
|
|
* one or the other of the input NaNs. Overflow is sibnaled when x
|
|
* is finite but nextafter(x,y) is infinite; underflow is signaled
|
|
* when nextafter(x,y) lies strictly between -2^Emin, 2^Emin; in
|
|
* both cases, inexact is signaled.
|
|
*
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
* O, U, I, P
|
|
*
|
|
*******************************************************************************/
|
|
|
|
double _nextafter(double x, double y)
|
|
{
|
|
unsigned int savedcw;
|
|
double result;
|
|
|
|
/* save user fp control word */
|
|
savedcw = _maskfp();
|
|
|
|
/* check for infinity or NAN */
|
|
if (IS_D_SPECIAL(x) || IS_D_SPECIAL(y)){
|
|
if (IS_D_SNAN(x) || IS_D_SNAN(y)){
|
|
return _except2(FP_I,OP_NEXTAFTER,x,y,_d_snan2(x,y),savedcw);
|
|
}
|
|
if (IS_D_QNAN(x) || IS_D_QNAN(y)){
|
|
return _handle_qnan2(OP_NEXTAFTER,x,y,savedcw);
|
|
}
|
|
|
|
//
|
|
// infinite arguments are not treated as special cases
|
|
//
|
|
}
|
|
|
|
if (y == x) {
|
|
|
|
//
|
|
// no exceptions are raised in this case
|
|
//
|
|
|
|
RETURN(savedcw, x);
|
|
}
|
|
|
|
if (x == 0) {
|
|
|
|
*D_LO(result) = 1;
|
|
|
|
if (y > x) {
|
|
*D_HI(result) = 0;
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// result should be negative
|
|
//
|
|
|
|
*D_HI(result) = (unsigned long)(1<<31);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// At this point x!=y, and x!=0. x can be treated as a 64bit
|
|
// integer in sign/magnitude representation. To get the next
|
|
// representable neighbor we add or subtract one from this
|
|
// integer. (Note that for boundary cases like x==INF, need to
|
|
// add one will never occur --this would mean that y should
|
|
// be greater than INF, which is impossible)
|
|
//
|
|
|
|
if (x > 0 && y < x ||
|
|
x < 0 && y > x) {
|
|
|
|
//
|
|
// decrease value by one
|
|
//
|
|
|
|
*D_LO(result) = *D_LO(x) - 1;
|
|
*D_HI(result) = *D_HI(x);
|
|
|
|
if (*D_LO(x) == 0) {
|
|
|
|
//
|
|
// a borrow should propagate to the high order dword
|
|
//
|
|
|
|
(*D_HI(result)) --;
|
|
}
|
|
}
|
|
|
|
else if (x > 0 && y > x ||
|
|
x < 0 && y < x) {
|
|
|
|
//
|
|
// increase value by one
|
|
//
|
|
|
|
*D_LO(result) = *D_LO(x) + 1;
|
|
*D_HI(result) = *D_HI(x);
|
|
|
|
if (*D_LO(result) == 0) {
|
|
|
|
//
|
|
// a carry should propagate to the high order dword
|
|
//
|
|
|
|
(*D_HI(result)) ++;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// check if an exception should be raised
|
|
//
|
|
|
|
|
|
if ( IS_D_DENORM(result) ) {
|
|
|
|
//
|
|
// should signal underflow and inexact
|
|
// and provide a properly scaled value
|
|
//
|
|
|
|
double mant;
|
|
int exp;
|
|
|
|
mant = _decomp(result, &exp);
|
|
result = _set_exp(mant, exp+IEEE_ADJUST);
|
|
|
|
return _except2(FP_U|FP_P,OP_NEXTAFTER,x,y,result,savedcw);
|
|
}
|
|
|
|
|
|
|
|
if ( IS_D_INF(result) || IS_D_MINF(result) ) {
|
|
|
|
//
|
|
// should signal overflow and inexact
|
|
// and provide a properly scaled value
|
|
//
|
|
|
|
double mant;
|
|
int exp;
|
|
|
|
mant = _decomp(result, &exp);
|
|
result = _set_exp(mant, exp-IEEE_ADJUST);
|
|
|
|
return _except2(FP_O|FP_P,OP_NEXTAFTER,x,y,result,savedcw);
|
|
}
|
|
|
|
|
|
RETURN(savedcw, result);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***
|
|
* _finite -
|
|
*
|
|
*Purpose:
|
|
* finite(x) returns the value TRUE if -INF < x < +INF and returns
|
|
* false otherwise [IEEE std]
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
* This routine is treated as a nonarithmetic operation, therefore
|
|
* it does not signal any floating point exceptions
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int _finite(double x)
|
|
{
|
|
if (IS_D_SPECIAL(x)) {
|
|
|
|
//
|
|
// x is INF or NaN
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***
|
|
* _isnan -
|
|
*
|
|
*Purpose:
|
|
* isnan(x) returns the value TRUE if x is a NaN, and returns FALSE
|
|
* otherwise.
|
|
*
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
* This routine is treated as a nonarithmetic operation, therefore
|
|
* it does not signal any floating point exceptions
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int _isnan(double x)
|
|
{
|
|
if (IS_D_SNAN(x) || IS_D_QNAN(x)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/***
|
|
*double _fpclass(double x) - floating point class
|
|
*
|
|
*Purpose:
|
|
* Compute the floating point class of a number, according
|
|
* to the recommendations of the IEEE std. 754
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
* This function is never exceptional, even when the argument is SNAN
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int _fpclass(double x)
|
|
{
|
|
int sign;
|
|
|
|
if (IS_D_SPECIAL(x)){
|
|
switch (_sptype(x)) {
|
|
case T_PINF:
|
|
return _FPCLASS_PINF;
|
|
case T_NINF:
|
|
return _FPCLASS_NINF;
|
|
case T_QNAN:
|
|
return _FPCLASS_QNAN;
|
|
default: //T_SNAN
|
|
return _FPCLASS_SNAN;
|
|
}
|
|
}
|
|
sign = (*D_EXP(x)) & 0x8000;
|
|
|
|
if (IS_D_DENORM(x))
|
|
return sign? _FPCLASS_ND : _FPCLASS_PD;
|
|
|
|
if (x == 0.0)
|
|
return sign? _FPCLASS_NZ : _FPCLASS_PZ;
|
|
|
|
return sign? _FPCLASS_NN : _FPCLASS_PN;
|
|
}
|