summaryrefslogtreecommitdiffstats
path: root/private/fp32/tran/alpha/sqrt.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/fp32/tran/alpha/sqrt.c')
-rw-r--r--private/fp32/tran/alpha/sqrt.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/private/fp32/tran/alpha/sqrt.c b/private/fp32/tran/alpha/sqrt.c
new file mode 100644
index 000000000..8cff2f7bf
--- /dev/null
+++ b/private/fp32/tran/alpha/sqrt.c
@@ -0,0 +1,192 @@
+/***
+*sqrt.c - square root
+*
+* Copyright (c) 1991-1991, Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+*
+*Revision History:
+* 8-15-91 GDP written
+* 1-29-91 GDP Kahan's algorithm for final rounding
+* 3-11-92 GDP new interval and initial approximation
+* 2-22-93 TVB Adapted for Alpha AXP
+*
+*******************************************************************************/
+#ifdef _ALPHA_
+
+//
+// This Alpha-specific version of sqrt is identical to portable version
+// except for instances where chopped floating point operations are performed
+// implicitly by setting the rounding mode to chopped. For Alpha, these
+// instances are replaced with calls to assembler routines that explicitly
+// perform the chopped operations. This is necessary because there is no
+// compiler support yet for generating floating point instructions using the
+// Alpha dynamic rounding mode.
+//
+
+#include <math.h>
+#include <trans.h>
+
+extern double _addtc(double x, double y);
+extern double _divtc(double x, double y);
+extern double _multc(double x, double y);
+extern double _subtc(double x, double y);
+
+//
+// Coefficients for initial approximation (Hart & al)
+//
+
+static double p00 = .2592768763e+0;
+static double p01 = .1052021187e+1;
+static double p02 = -.3163221431e+0;
+
+
+/***
+*double sqrt(double x) - square root
+*
+*Purpose:
+* Compute the square root of a number.
+* This function should be provided by the underlying
+* hardware (IEEE spec).
+*Entry:
+*
+*Exit:
+*
+*Exceptions:
+* I P
+*******************************************************************************/
+double sqrt(double x)
+{
+ unsigned int savedcw, sw;
+ double result,t;
+ unsigned int stat,rc;
+
+ savedcw = _ctrlfp(ICW, IMCW);
+
+ if (IS_D_SPECIAL(x)){
+ switch (_sptype(x)) {
+ case T_PINF:
+ RETURN(savedcw, x);
+ case T_QNAN:
+ return _handle_qnan1(OP_SQRT, x, savedcw);
+ case T_SNAN:
+ return _except1(FP_I,OP_SQRT,x,QNAN_SQRT,savedcw);
+ }
+ /* -INF will be handled in the x<0 case */
+ }
+ if (x < 0.0) {
+ return _except1(FP_I, OP_SQRT, x, QNAN_SQRT,savedcw);
+ }
+
+ if (x == 0.0) {
+ RETURN (savedcw, x);
+ }
+
+
+ result = _fsqrt(x);
+
+
+ //
+ // Kahan's algorithm
+ //
+
+ t = _divtc(x, result);
+
+ //
+ // Multiply back to see if division was exact.
+ // Compare using subtraction to avoid invalid exceptions.
+ //
+
+ if (_subtc(x, _multc(t, result)) == 0.0) {
+ // exact
+ if (t == result) {
+ RETURN(savedcw, result);
+ }
+ else {
+ // t = t-1
+ if (*D_LO(t) == 0) {
+ (*D_HI(t)) --;
+ }
+ (*D_LO(t)) --;
+ }
+
+ }
+
+ rc = savedcw & IMCW_RC;
+ if (rc == IRC_UP || rc == IRC_NEAR) {
+ // t = t+1
+ (*D_LO(t)) ++;
+ if (*D_LO(t) == 0) {
+ (*D_HI(t)) ++;
+ }
+ if (rc == IRC_UP) {
+ // y = y+1
+ (*D_LO(t)) ++;
+ if (*D_LO(t) == 0) {
+ (*D_HI(t)) ++;
+ }
+ }
+ }
+
+ result = _multc(0.5, _addtc(t, result));
+
+ _set_statfp(ISW_INEXACT); // update status word
+ RETURN_INEXACT1(OP_SQRT, x, result, savedcw);
+}
+
+
+
+/***
+* _fsqrt - non IEEE conforming square root
+*
+*Purpose:
+* compute a square root of a normal number without performing
+* IEEE rounding. The argument is a finite number (no NaN or INF)
+*
+*Entry:
+*
+*Exit:
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+double _fsqrt(double x)
+{
+ double f,y,result;
+ int n;
+
+ f = _decomp(x,&n);
+
+ if (n & 0x1) {
+ // n is odd
+ n++;
+ f *= 0.5;
+ }
+
+ //
+ // approximation for sqrt in the interval [.25, 1]
+ // (Computer Approximationsn, Hart & al.)
+ // gives more than 7 bits of accuracy
+ //
+
+ y = p00 + f * (p01 + f * p02);
+
+ y += f / y;
+ y *= 0.5;
+
+ y += f / y;
+ y *= 0.5;
+
+ y += f / y;
+ y *= 0.5;
+
+ n >>= 1;
+ result = _add_exp(y,n);
+
+ return result;
+}
+
+
+
+#endif