/*---------------------------------------------------------------------------+ | wm_sqrt.c | | | | Fixed point arithmetic square root evaluation. | | | | Copyright (C) 1992,1993,1995,1997,1999 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@melbpc.org.au | | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ | returns the square root of n in n. | | | | Use Newton's method to compute the square root of a number, which must | | be in the range [1.0 .. 4.0), to 64 bits accuracy. | | Does not check the sign or tag of the argument. | | Sets the exponent, but not the sign or tag of the result. | | | | The guess is kept in %esi:%edi | +---------------------------------------------------------------------------*/ #include "exception.h" #include "fpu_emu.h" /* The following value indicates the trailing bits (of 96 bits) which may be in error when the final Newton iteration is finished (0x20 corresponds to the last 5 bits in error, i.e. 91 bits precision). A check of the following code with more than 3 billion (3.0e9) random and selected values showed that 0x10 was always a large enough value, so 0x20 should be a conservative choice. */ #define ERR_MARGIN 0x20 int wm_sqrt(FPU_REG *n, s32 dummy1, s32 dummy2, u16 control_w, u8 sign) { u64 nn, guess, halfn, lowr, mid, upr, diff, uwork; s64 work; u32 ne, guess32, work32, diff32, mid32; int shifted; nn = significand(n); ne = 0; if ( exponent16(n) == EXP_BIAS ) { /* Shift the argument right one position. */ if ( nn & 1 ) ne = 0x80000000; nn >>= 1; guess = n->sigh >> 2; shifted = 1; } else { guess = n->sigh >> 1; shifted = 0; } guess += 0x40000000; guess *= 0xaaaaaaaa; guess <<= 1; guess32 = guess >> 32; if ( !(guess32 & 0x80000000) ) guess32 = 0x80000000; halfn = nn >> 1; guess32 = halfn / guess32 + (guess32 >> 1); guess32 = halfn / guess32 + (guess32 >> 1); guess32 = halfn / guess32 + (guess32 >> 1); /* * Now that an estimate accurate to about 30 bits has been obtained, * we improve it to 60 bits or so. * * The strategy from now on is to compute new estimates from * guess := guess + (n - guess^2) / (2 * guess) */ work = guess32; work = nn - work * guess32; work <<= 28; /* 29 - 1 */ work /= guess32; work <<= 3; /* 29 + 3 = 32 */ work += ((u64)guess32) << 32; if ( work == 0 ) /* This happens in one or two special cases */ work = 0xffffffffffffffffLL; guess = work; /* guess is now accurate to about 60 bits */ if ( work > 0 ) { #ifdef PARANOID if ( (n->sigh != 0xffffffff) && (n->sigl != 0xffffffff) ) { EXCEPTION(EX_INTERNAL|0x213); } #endif /* We know the answer here. */ return FPU_round(n, 0x7fffffff, 0, control_w, sign); } /* Refine the guess to significantly more than 64 bits. */ /* First, square the current guess. */ guess32 = guess >> 32; work32 = guess; /* lower 32 times lower 32 */ lowr = work32; lowr *= work32; /* lower 32 times upper 32 */ mid = guess32; mid *= work32; /* upper 32 times upper 32 */ upr = guess32; upr *= guess32; /* upper 32 bits of the middle product times 2 */ upr += mid >> (32-1); /* lower 32 bits of the middle product times 2 */ work32 = mid << 1; /* upper 32 bits of the lower product */ mid32 = lowr >> 32; mid32 += work32; if ( mid32 < work32 ) upr ++; /* We now have the first 96 bits (truncated) of the square of the guess */ diff = upr - nn; diff32 = mid32 - ne; if ( diff32 > mid32 ) diff --; if ( ((s64)diff) < 0 ) { /* The difference is negative, negate it. */ diff32 = -((s32)diff32); diff = ~diff; if ( diff32 == 0 ) diff ++; #ifdef PARANOID if ( (diff >> 32) != 0 ) { EXCEPTION(EX_INTERNAL|0x207); } #endif diff <<= 32; diff |= diff32; work32 = diff / guess32; work = work32; work <<= 32; diff = diff % guess32; diff <<= 32; work32 = diff / guess32; work |= work32; work >>= 1; work32 = work >> 32; guess += work32; /* The first 64 bits */ guess32 = work; /* The next 32 bits */ /* The guess should now be good to about 90 bits */ } else { /* The difference is positive. */ diff <<= 32; diff |= diff32; work32 = diff / guess32; work = work32; work <<= 32; diff = diff % guess32; diff <<= 32; work32 = diff / guess32; work |= work32; work >>= 1; work32 = work >> 32; guess32 = work; /* The last 32 bits (of 96) */ guess32 = -guess32; if ( guess32 ) guess --; guess -= work32; /* The first 64 bits */ /* The guess should now be good to about 90 bits */ } setexponent16(n, 0); if ( guess32 >= (u32) -ERR_MARGIN ) { /* Nearly exact, we round the 64 bit result upward. */ guess ++; } else if ( (guess32 > ERR_MARGIN) && ((guess32 < 0x80000000-ERR_MARGIN) || (guess32 > 0x80000000+ERR_MARGIN)) ) { /* We have enough accuracy to decide rounding */ significand(n) = guess; return FPU_round(n, guess32, 0, control_w, sign); } if ( (guess32 <= ERR_MARGIN) || (guess32 >= (u32) -ERR_MARGIN) ) { /* * This is an easy case because x^1/2 is monotonic. * We need just find the square of our estimate, compare it * with the argument, and deduce whether our estimate is * above, below, or exact. We use the fact that the estimate * is known to be accurate to about 90 bits. */ /* We compute the lower 64 bits of the 128 bit product */ work32 = guess; lowr = work32; lowr *= work32; uwork = guess >> 32; work32 = guess; uwork *= work32; uwork <<= 33; /* 33 = 32+1 (for two times the product) */ lowr += uwork; /* We now have the 64 bits */ /* We need only look at bits 65..96 of the square of guess. */ if ( shifted ) work32 = lowr >> 31; else work32 = lowr >> 32; #ifdef PARANOID if ( ((s32)work32 > 3*ERR_MARGIN) || ((s32)work32 < -3*ERR_MARGIN) ) { EXCEPTION(EX_INTERNAL|0x214); } #endif significand(n) = guess; if ( (s32)work32 > 0 ) { /* guess is too large */ significand(n) --; return FPU_round(n, 0xffffff00, 0, control_w, sign); } else if ( (s32)work32 < 0 ) { /* guess is a little too small */ return FPU_round(n, 0x000000ff, 0, control_w, sign); } else if ( (u32)lowr != 0 ) { /* guess is too large */ significand(n) --; return FPU_round(n, 0xffffff00, 0, control_w, sign); } /* Our guess is precise. */ return FPU_round(n, 0, 0, control_w, sign); } /* Very similar to the case above, but the last bit is near 0.5. We handle this just like the case above but we shift everything by one bit. */ uwork = guess; uwork <<= 1; uwork |= 1; /* add the half bit */ /* We compute the lower 64 bits of the 128 bit product */ work32 = uwork; lowr = work32; lowr *= work32; work32 = uwork >> 32; uwork &= 0xffffffff; uwork *= work32; uwork <<= 33; /* 33 = 32+1 (for two times the product) */ lowr += uwork; /* We now have the 64 bits. The lowest 32 bits of lowr are not all zero (the lsb is 1). */ /* We need only look at bits 65..96 of the square of guess. */ if ( shifted ) work32 = lowr >> 31; else work32 = lowr >> 32; #ifdef PARANOID if ( ((s32)work32 > 4*3*ERR_MARGIN) || ((s32)work32 < -4*3*ERR_MARGIN) ) { EXCEPTION(EX_INTERNAL|0x215); } #endif significand(n) = guess; if ( (s32)work32 < 0 ) { /* guess plus half bit is a little too small */ return FPU_round(n, 0x800000ff, 0, control_w, sign); } else /* Note that the lower 64 bits of the product are not all zero */ { /* guess plus half bit is too large */ return FPU_round(n, 0x7fffff00, 0, control_w, sign); } /* Note that the result of a square root cannot have precisely a half bit of a least significant place (it is left as an exercise for the reader to prove this! (hint: 65 bit*65 bit => n bits)). */ }