singrdk/base/Kernel/Native/arm/Math.cpp

848 lines
22 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Math.cpp
//
// Note:
//
#define LITTL_ENDIAN
#include "hal.h"
#pragma warning(disable: 4725)
//////////////////////////////////////////////////////////////////////////////
//
#define IMCW_EM 0x003f // interrupt Exception Masks
#define IEM_INVALID 0x0001 // invalid
#define IEM_DENORMAL 0x0002 // denormal
#define IEM_ZERODIVIDE 0x0004 // zero divide
#define IEM_OVERFLOW 0x0008 // overflow
#define IEM_UNDERFLOW 0x0010 // underflow
#define IEM_PRECISION 0x0020 // precision
#define IMCW_RC 0x0c00 // Rounding Control
#define IRC_CHOP 0x0c00 // chop
#define IRC_UP 0x0800 // up
#define IRC_DOWN 0x0400 // down
#define IRC_NEAR 0x0000 // near
#define ISW_INVALID 0x0001 // invalid
#define ISW_DENORMAL 0x0002 // denormal
#define ISW_ZERODIVIDE 0x0004 // zero divide
#define ISW_OVERFLOW 0x0008 // overflow
#define ISW_UNDERFLOW 0x0010 // underflow
#define ISW_PRECISION 0x0020 // inexact
#define IMCW_PC 0x0300 // Precision Control
#define IPC_24 0x0000 // 24 bits
#define IPC_53 0x0200 // 53 bits
#define IPC_64 0x0300 // 64 bits
//////////////////////////////////////////////////////////////////////////////
//
#ifdef BIG_ENDIAN
// big endian
#define D_EXP(x) ((unsigned short *)&(x))
#define D_HI(x) ((unsigned long *)&(x))
#define D_LO(x) ((unsigned long *)&(x)+1)
#else
#define D_EXP(x) ((unsigned short *)&(x)+3)
#define D_HI(x) ((unsigned long *)&(x)+1)
#define D_LO(x) ((unsigned long *)&(x))
#endif
#define D_BIASM1 0x3fe // off by one to compensate for the implied bit
#define MAXEXP 1024
#define MINEXP -1021
// return the int representation of the exponent
// if x = .f * 2^n, 0.5<=f<1, return n (unbiased)
// e.g. INTEXP(3.0) == 2
#define INTEXP(x) ((signed short)((*D_EXP(x) & 0x7ff0) >> 4) - D_BIASM1)
// check for infinity, NAN
#define D_ISINF(x) ((*D_HI(x) & 0x7fffffff) == 0x7ff00000 && *D_LO(x) == 0)
#define IS_D_SPECIAL(x) ((*D_EXP(x) & 0x7ff0) == 0x7ff0)
#define IS_D_NAN(x) (IS_D_SPECIAL(x) && !D_ISINF(x))
#define IS_D_QNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff8)
#define IS_D_SNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff0 && \
(*D_HI(x) << 13 || *D_LO(x)))
#define IS_D_DENORM(x) ((*D_EXP(x) & 0x7ff0) == 0 && \
(*D_HI(x) << 12 || *D_LO(x)))
#define IS_D_INF(x) (*D_HI(x) == 0x7ff00000 && *D_LO(x) == 0)
#define IS_D_MINF(x) (*D_HI(x) == 0xfff00000 && *D_LO(x) == 0)
/////////////////////////////////////////////////////// Define special values.
//
typedef union {
uint64 lng;
float64 dbl;
} _dbl;
static _dbl _d_pos_inf = { 0x7ff0000000000000 }; // positive infinity
static _dbl _d_neg_inf = { 0xfff0000000000000 }; // negative infinity
static _dbl _d_ind = { 0xfff8000000000000 }; // real indefinite
static _dbl _d_neg_zer = { 0x8000000000000000 }; // negative zero
//////////////////////////////////////////////////////////////////////////////
//
uint16 g_ClearFp()
{
__debugbreak();
return 0;
}
// Doesn't alter any additional registers
uint16 g_ControlFp(uint16 newctrl, uint16 mask)
{
__debugbreak();
return 0;
}
// Doesn't alter any additional registers
void g_RestoreFp(uint16 newctrl)
{
__debugbreak();
}
//////////////////////////////////////////////////////////////////////////////
//
static float64 _frnd(float64 v)
{
__debugbreak();
return 0;
}
static int32 _ftoi(float64 v)
{
__debugbreak();
return 0;
}
static float64 _abs(float64 x)
{
(*(uint64 *)&x) &= 0x7fffffffffffffff;
return x;
}
static float64 _set_exp(float64 x, int exp) // does not check validity of exp
{
float64 retval = x;
int biased_exp = exp + D_BIASM1;
*D_EXP(retval) = (unsigned short) (*D_EXP(x) & 0x800f | (biased_exp << 4));
return retval;
}
int _get_exp(float64 x)
{
signed short exp;
exp = (signed short)((*D_EXP(x) & 0x7ff0) >> 4);
exp -= D_BIASM1; //unbias
return (int) exp;
}
// Provide the mantissa and the exponent of e^x
//
// Entry:
// x : a (non special) float64 precision number
//
// Exit:
// *newexp: the exponent of e^x
// return value: the mantissa m of e^x scaled by a factor
// (the value of this factor has no significance.
// The mantissa can be obtained with _set_exp(m, 0).
//
// _set_exp(m, *pnewexp) may be used for constructing the final
// result, if it is within the representable range.
//
static inline float64 r_exp_p(float64 z)
{
static float64 const p0 = 0.249999999999999993e+0;
static float64 const p1 = 0.694360001511792852e-2;
static float64 const p2 = 0.165203300268279130e-4;
return ( (p2 * (z) + p1) * (z) + p0 );
}
static inline float64 r_exp_q(float64 z)
{
static float64 const q0 = 0.500000000000000000e+0;
static float64 const q1 = 0.555538666969001188e-1;
static float64 const q2 = 0.495862884905441294e-3;
return ( (q2 * (z) + q1) * (z) + q0 );
}
// These are referenced by the ARM compiler compiling the code below - don't know why
// but workaround for now.
extern "C" double __imp___muld(double, double)
{
__debugbreak();
return 0;
}
extern "C" double __imp___divd(double, double)
{
__debugbreak();
return 0;
}
extern "C" double __imp___addd(double, double)
{
__debugbreak();
return 0;
}
extern "C" double __imp___negd(double)
{
__debugbreak();
return 0;
}
float64 _exphlp(float64 x, int * pnewexp)
{
static float64 const LN2INV = 1.442695040889634074; // 1/ln(2)
static float64 const C1 = 0.693359375000000000;
static float64 const C2 = -2.1219444005469058277e-4;
float64 xn;
float64 g,z,gpz,qz,rg;
int n;
xn = _frnd(x * LN2INV);
n = _ftoi(xn);
// assume guard digit is present
g = (x - xn * C1) - xn * C2;
z = g*g;
gpz = g * r_exp_p(z);
qz = r_exp_q(z);
rg = 0.5 + gpz/(qz-gpz);
n++;
*pnewexp = _get_exp(rg) + n;
return rg;
}
//
// Decompose a number to a normalized mantissa and exponent.
//
static float64 _decomp(float64 x, int *pexp)
{
int exp;
float64 man;
if (x == 0.0) {
man = 0.0;
exp = 0;
}
else if (IS_D_DENORM(x)) {
int neg;
exp = 1 - D_BIASM1;
neg = x < 0.0;
while((*D_EXP(x) & 0x0010) == 0) {
// shift mantissa to the left until bit 52 is 1
(*D_HI(x)) <<= 1;
if (*D_LO(x) & 0x80000000)
(*D_HI(x)) |= 0x1;
(*D_LO(x)) <<= 1;
exp--;
}
(*D_EXP(x)) &= 0xffef; // clear bit 52
if (neg) {
(*D_EXP(x)) |= 0x8000; // set sign bit
}
man = _set_exp(x,0);
}
else {
man = _set_exp(x,0);
exp = INTEXP(x);
}
*pexp = exp;
return man;
}
static bool is_odd_integer(float64 y)
{
int exp = INTEXP(y);
if (exp < 1 || exp > 63) {
return false;
}
return(((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0x8000000000000000);
}
static bool is_even_integer(float64 y)
{
int exp = INTEXP(y);
if (exp < 1 || exp > 63) {
return false;
}
return (((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0);
}
//////////////////////////////////////////////////////////////////////////////
float64 Class_System_Math::g_Round(float64 v)
{
return _frnd(v);
}
float64 Class_System_Math::g_Sin(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Cos(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Tan(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Atan(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Atan2(float64 v, float64 w)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Abs(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Sqrt(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Log(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Log10(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Exp(float64 v)
{
__debugbreak();
return 0;
}
// constants for the rational approximation
static inline float64 r_sinh_p(float64 f)
{
static float64 const p0 = -0.35181283430177117881e+6;
static float64 const p1 = -0.11563521196851768270e+5;
static float64 const p2 = -0.16375798202630751372e+3;
static float64 const p3 = -0.78966127417357099479e+0;
return (((p3 * (f) + p2) * (f) + p1) * (f) + p0);
}
static inline float64 r_sinh_q(float64 f)
{
static float64 const q0 = -0.21108770058106271242e+7;
static float64 const q1 = 0.36162723109421836460e+5;
static float64 const q2 = -0.27773523119650701667e+3;
// q3 = 1 is not used (avoid multiplication by 1)
return ((((f) + q2) * (f) + q1) * (f) + q0);
}
// Compute the hyperbolic sine of a number.
// The algorithm (reduction / rational approximation) is
// taken from Cody & Waite.
float64 Class_System_Math::g_Sinh(float64 v)
{
static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2
// exp(YBAR) should be close to but less than XMAX
// and 1/exp(YBAR) should not underflow
static float64 const YBAR = 7.00e2;
// WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX
static float64 const WMAX = 1.77514678223345998953e+003;
float64 result;
if (IS_D_SPECIAL(v)) {
if (IS_D_INF(v) || IS_D_MINF(v)) {
}
else if (IS_D_QNAN(v)) {
// TODO: should throw a soft exception.
}
else if (IS_D_SNAN(v)) {
// TODO: should throw a hard exception.
}
result = v;
}
else if (v == 0.0) {
// no precision exception
result = v;
}
else {
bool neg = (v < 0.0);
float64 y = _abs(v);
if (y > 1.0) {
int newexp;
if (y > YBAR) {
if (y > WMAX) {
// result too large, even after scaling
result = v * _d_pos_inf.dbl;
// TODO should throw hard exception.
goto exit;
}
//
// result = exp(y)/2
//
result = _exphlp(y, &newexp);
newexp --; //divide by 2
if (newexp > MAXEXP) {
result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST);
// TODO should throw hard exception.
goto exit;
}
else {
result = _set_exp(result, newexp);
}
}
else {
float64 z = _exphlp(y, &newexp);
z = _set_exp(z, newexp);
result = (z - 1.0/z) / 2.0;
}
if (neg) {
result = -result;
}
}
else {
if (y < EPS) {
result = v;
if (IS_D_DENORM(result)) {
result = 0.0; // result = _add_exp(result, IEEE_ADJUST);
// TODO should throw hard exception.
goto exit;
}
}
else {
float64 f = v * v;
float64 r = f * (r_sinh_p(f) / r_sinh_q(f));
result = v + v * r;
}
}
}
exit:
return result;
}
// Compute the hyperbolic cosine of a number.
// The algorithm (reduction / rational approximation) is
// taken from Cody & Waite.
//
float64 Class_System_Math::g_Cosh(float64 v)
{
// exp(YBAR) should be close to but less than XMAX
// and 1/exp(YBAR) should not underflow
static float64 const YBAR = 7.00e2;
// WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX
static float64 const WMAX = 1.77514678223345998953e+003;
float64 result;
if (IS_D_SPECIAL(v)) {
if (IS_D_INF(v) || IS_D_MINF(v)) {
result = _d_pos_inf.dbl;
}
else if (IS_D_QNAN(v)) {
// TODO: should throw a soft exception.
result = v;
}
else {
// TODO: should throw a hard exception.
result = v;
}
}
else if (v == 0.0) {
result = 1.0;
}
else {
float64 y = _abs(v);
if (y > YBAR) {
if (y > WMAX) {
// TODO: should throw a hard exception.
result = v;
goto exit;
}
//
// result = exp(y)/2
//
int newexp;
result = _exphlp(y, &newexp);
newexp --; //divide by 2
if (newexp > MAXEXP) {
// TODO: should throw a hard exception.
result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST);
goto exit;
}
else {
result = _set_exp(result, newexp);
}
}
else {
int newexp;
float64 z = _exphlp(y, &newexp);
z = _set_exp(z, newexp);
result = (z + 1.0/z) / 2.0;
}
// TODO: should throw a hard exception if exactness is required.
}
exit:
return result;
}
// Compute the hyperbolic tangent of a number.
// The algorithm (reduction / rational approximation) is
// taken from Cody & Waite.
static inline float64 r_tanh(float64 g)
{
// constants for rational approximation
static float64 const p0 = -0.16134119023996228053e+4;
static float64 const p1 = -0.99225929672236083313e+2;
static float64 const p2 = -0.96437492777225469787e+0;
static float64 const q0 = 0.48402357071988688686e+4;
static float64 const q1 = 0.22337720718962312926e+4;
static float64 const q2 = 0.11274474380534949335e+3;
static float64 const q3 = 0.10000000000000000000e+1;
return ((((p2 * (g) + p1) * (g) + p0) * (g)) / ((((g) + q2) * (g) + q1) * (g) + q0));
}
float64 Class_System_Math::g_Tanh(float64 v)
{
// constants
static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2
static float64 const XBIG = 1.90615474653984960096e+001; // ln(2)(53+2)/2
static float64 const C0 = 0.54930614433405484570; // ln(3)/2
if (IS_D_SPECIAL(v)) {
if (IS_D_INF(v)) {
v = 1.0;
}
else if (IS_D_MINF(v)) {
v = -1.0;
}
else if (IS_D_QNAN(v)) {
// TODO: should throw a soft exception.
}
else if (IS_D_SNAN(v)) {
// TODO: should throw a hard exception.
}
}
else if (v == 0.0) {
// no precision exception
}
else {
bool neg = false;
if (v < 0.0) {
neg = true;
v = -v;
}
if (v > XBIG) {
v = 1;
}
else if (v > C0) {
v = 0.5 - 1.0 / (g_Exp(v+v) + 1.0);
v = v + v;
}
else if (v < EPS) {
if (IS_D_DENORM(v)) {
// TODO: should throw a heard exception.
}
}
else {
v = v + v * r_tanh(v * v);
}
if (neg) {
v = -v;
}
}
return v;
}
float64 Class_System_Math::g_Acos(float64 v)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Asin(float64 v)
{
__debugbreak();
return 0;
}
static float64 _fastpow(float64 v, float64 w)
{
__debugbreak();
return 0;
}
float64 Class_System_Math::g_Pow(float64 v, float64 w)
{
float64 result = 0.0;
// check for infinity or NAN
if (IS_D_SPECIAL(w) || IS_D_SPECIAL(v)) {
if (IS_D_INF(w)) {
float64 absv = _abs(v);
if (absv > 1.0) {
result = _d_pos_inf.dbl;
}
else if (absv < 1.0) {
result = 0.0;
}
else {
result = _d_ind.dbl;
// TODO: Should throw a hard exception.
goto exit;
}
}
else if (IS_D_MINF(w)) {
float64 absv = _abs(v);
if (absv > 1.0) {
result = 0.0;
}
else if (absv < 1.0) {
result = _d_pos_inf.dbl;
}
else {
result = _d_ind.dbl;
// TODO: Should throw a hard exception.
goto exit;
}
}
else if (IS_D_INF(v)) {
if (w > 0.0) {
result = _d_pos_inf.dbl;
}
else if (w < 0.0) {
result = 0.0;
}
else {
result = 1.0;
}
}
else if (IS_D_MINF(v)) {
if (w > 0.0) {
result = is_odd_integer(w) ? _d_neg_inf.dbl : _d_pos_inf.dbl;
}
else if (w < 0.0) {
result = is_odd_integer(w) ? _d_neg_zer.dbl : 0.0;
}
else {
result = 1.0;
}
}
}
else if (w == 0.0) {
result = 1.0;
}
else if (v == 0.0) {
if (w < 0.0) {
// TODO: Should throw a hard exception.
result = is_odd_integer(v) ? _d_neg_inf.dbl : _d_pos_inf.dbl;
}
else {
result = is_odd_integer(v) ? w : 0.0;
}
}
else if (v < 0.0) {
if (is_odd_integer(w)) {
result = - _fastpow(-v, w);
}
else if (is_even_integer(w)) {
result = _fastpow(-v, w);
}
else {
// TODO: Should throw a hard exception.
result = v;
}
}
else {
result = _fastpow(v, w);
}
exit:
return result;
}
float64 Class_System_Math::g_Mod(float64 x, float64 y)
{
float64 result = 0.0;
// check for infinity or NAN
if (IS_D_SPECIAL(y) || IS_D_SPECIAL(x)) {
if (IS_D_SNAN(y) || IS_D_SNAN(x)) {
// TODO: Should throw a hard exception.
}
else if (IS_D_QNAN(y) || IS_D_QNAN(x)) {
// TODO: Should throw a soft exception.
result = x;
}
else if (IS_D_INF(x) || IS_D_MINF(x)) {
// TODO: Should throw a hard exception.
}
}
else if (y == 0.0) {
// TODO: Should throw a hard exception.
}
else if (x == 0.0) {
result = x;
}
else {
const int SCALE = 53;
bool neg = false;
bool denorm = false;
float64 d,ty,fx,fy;
int nx, ny, nexp;
if (x < 0.0) {
result = -x;
neg = 1;
}
else {
result = x;
}
ty = _abs(y);
while (result >= ty) {
fx = _decomp(result, &nx);
fy = _decomp(ty, &ny);
if (nx < MINEXP) {
// result is a denormalized number
denorm = true;
nx += SCALE;
ny += SCALE;
result = _set_exp(fx, nx);
ty = _set_exp(fy, ny);
}
if (fx >= fy) {
nexp = nx ;
}
else {
nexp = nx - 1;
}
d = _set_exp(fy, nexp);
result -= d;
}
if (denorm) {
// TODO: should raise only FP_U exception.
}
if (neg) {
result = -result;
}
}
return result;
}
float64 Class_System_Math::g_Floor(float64 v)
{
float64 result;
// save user fp control word, and set round down.
uint16 oldcw = g_ControlFp(IRC_DOWN, IMCW_RC);
if (IS_D_SPECIAL(v)) {
if (IS_D_QNAN(v)) {
// TODO: should throw a soft exception.
}
else if (IS_D_SNAN(v)) {
// TODO: should throw a hard exception.
}
result = v;
}
else {
result = _frnd(v); // round according to the current rounding mode.
}
g_RestoreFp(oldcw);
return result;
}
float64 Class_System_Math::g_Ceiling(float64 v)
{
float64 result;
// save user fp control word, and set round up.
uint16 oldcw = g_ControlFp(IRC_UP, IMCW_RC);
if (IS_D_SPECIAL(v)) {
if (IS_D_QNAN(v)) {
// TODO: should throw a soft exception.
}
else if (IS_D_SNAN(v)) {
// TODO: should throw a hard exception.
}
result = v;
}
else {
result = _frnd(v); // round according to the current rounding mode.
}
g_RestoreFp(oldcw);
return result;
}
//
///////////////////////////////////////////////////////////////// End of File.