118 lines
2.6 KiB
C
118 lines
2.6 KiB
C
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "fpu_cw.h"
|
|
#include "fpu_sw.h"
|
|
|
|
#define FPUSW_FLAG_MASK \
|
|
(FPUSW_CONDITION_C0 | FPUSW_CONDITION_C2 | FPUSW_CONDITION_C3)
|
|
|
|
#define DOUBLE_NORMAL_MIN 2.2250738585072013830902327173324e-308 /* 2^-1022 */
|
|
|
|
int fpclassify(double x)
|
|
{
|
|
/* use status word returned by fpu_xam to determine type */
|
|
switch (fpu_xam(x) & FPUSW_FLAG_MASK)
|
|
{
|
|
case FPUSW_CONDITION_C0:
|
|
return FP_NAN;
|
|
|
|
case FPUSW_CONDITION_C2:
|
|
/*
|
|
* unfortunately, fxam always operates on long doubles
|
|
* regardless of the precision setting. This means some
|
|
* subnormals are incorrectly classified as normals,
|
|
* since they can be normalized using the additional
|
|
* exponent bits available. However, if we already know
|
|
* that the number is normal as a long double, finding
|
|
* out whether it would be subnormal as a double is just
|
|
* a simple comparison.
|
|
*/
|
|
if (-DOUBLE_NORMAL_MIN < x && x < DOUBLE_NORMAL_MIN)
|
|
return FP_SUBNORMAL;
|
|
else
|
|
return FP_NORMAL;
|
|
|
|
case FPUSW_CONDITION_C0 | FPUSW_CONDITION_C2:
|
|
return FP_INFINITE;
|
|
|
|
case FPUSW_CONDITION_C3:
|
|
return FP_ZERO;
|
|
|
|
case FPUSW_CONDITION_C3 | FPUSW_CONDITION_C2:
|
|
return FP_SUBNORMAL;
|
|
}
|
|
|
|
/* we don't expect any other case: unsupported, emtpy or reserved */
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
|
|
int signbit(double x)
|
|
{
|
|
u16_t sw;
|
|
|
|
/* examine and use status word to determine sign */
|
|
sw = fpu_xam(x);
|
|
return (sw & FPUSW_CONDITION_C1) ? 1 : 0;
|
|
}
|
|
|
|
#define FPUSW_GREATER 0
|
|
#define FPUSW_LESS FPUSW_CONDITION_C0
|
|
#define FPUSW_EQUAL FPUSW_CONDITION_C3
|
|
#define FPUSW_UNORDERED \
|
|
(FPUSW_CONDITION_C0 | FPUSW_CONDITION_C2 | FPUSW_CONDITION_C3)
|
|
|
|
static int fpcompare(double x, double y, u16_t sw1, u16_t sw2)
|
|
{
|
|
u16_t sw;
|
|
|
|
/* compare and check sanity */
|
|
sw = fpu_compare(x, y) & FPUSW_FLAG_MASK;
|
|
switch (sw)
|
|
{
|
|
case FPUSW_GREATER:
|
|
case FPUSW_LESS:
|
|
case FPUSW_EQUAL:
|
|
case FPUSW_UNORDERED:
|
|
break;
|
|
|
|
default:
|
|
/* other values are not possible (see IA32 Dev Man) */
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
|
|
/* test whether FPUSW equals either sw1 or sw2 */
|
|
return sw == sw1 || sw == sw2;
|
|
}
|
|
|
|
int isgreater(double x, double y)
|
|
{
|
|
return fpcompare(x, y, FPUSW_GREATER, -1);
|
|
}
|
|
|
|
int isgreaterequal(double x, double y)
|
|
{
|
|
return fpcompare(x, y, FPUSW_GREATER, FPUSW_EQUAL);
|
|
}
|
|
|
|
int isless(double x, double y)
|
|
{
|
|
return fpcompare(x, y, FPUSW_LESS, -1);
|
|
}
|
|
|
|
int islessequal(double x, double y)
|
|
{
|
|
return fpcompare(x, y, FPUSW_LESS, FPUSW_EQUAL);
|
|
}
|
|
|
|
int islessgreater(double x, double y)
|
|
{
|
|
return fpcompare(x, y, FPUSW_LESS, FPUSW_GREATER);
|
|
}
|
|
|
|
int isunordered(double x, double y)
|
|
{
|
|
return fpcompare(x, y, FPUSW_UNORDERED, -1);
|
|
}
|