summaryrefslogtreecommitdiff
path: root/sqrt3.c
blob: 072c406d7dd2a07a10efd03c773877d85da21ed5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "gmp.h"
#include "gmp-impl.h"
#include "mpfr.h"
#include "longlong.h"

/* #define DEBUG */

void
mpfr_sqrt3 (mpfr_ptr r, mpfr_srcptr u, unsigned char rnd_mode)
{
  mp_ptr up, rp, tmp;
  mp_size_t usize, rrsize;
  mp_size_t rsize;
  mp_size_t prec, err;
  mp_limb_t q_limb;
  long rw, nw; 
  unsigned long cc = 0; 
  char can_round = 0; 
  TMP_DECL (marker); TMP_DECL(marker0); 

  if (FLAG_NAN(u) || SIGN(u) == -1) { SET_NAN(r); return; }
  
  prec = PREC(r);

  if (!NOTZERO(u))
    {
      EXP(r) = 0; 
      MPN_ZERO(MANT(r), ABSSIZE(r)); 
      return; 
    }

  up = MANT(u);

#ifdef DEBUG
      printf("Entering square root : "); 
      for(k = usize - 1; k >= 0; k--) { printf("%lu ", up[k]); }
      printf(".\n"); 
#endif

  /* Compare the mantissas */
  
  EXP(r) = ((EXP(u) + (EXP(u) & 1)) / 2) ;  
  
  usize = (PREC(u) - 1)/BITS_PER_MP_LIMB + 1; 
  rsize = ((PREC(r) + 2 + (EXP(u) & 1))/BITS_PER_MP_LIMB + 1) << 1; 
  rrsize = (PREC(r) + 2 + (EXP(u) & 1))/BITS_PER_MP_LIMB + 1;
  /* One extra bit is needed in order to get the square root with enough
     precision ; take one extra bit for rrsize in order to solve more 
     easily the problem of rounding to nearest.
     Need to have 2*rrsize = rsize...
     Take one extra bit if the exponent of u is odd since we shall have
     to shift then.
  */

  TMP_MARK(marker0); 
  if (EXP(u) & 1) /* Shift u one bit to the right */
    {
      if (PREC(u) & (BITS_PER_MP_LIMB - 1))
	{
	  up = TMP_ALLOC(usize*BYTES_PER_MP_LIMB);
	  mpn_rshift(up, u->_mp_d - usize + ABSSIZE(u), usize, 1); 
	}
      else
	{
	  up = TMP_ALLOC((usize + 1)*BYTES_PER_MP_LIMB);
	  if (mpn_rshift(up + 1, u->_mp_d - usize + ABSSIZE(u), ABSSIZE(u), 1))
	    up [0] = ((mp_limb_t) 1) << (BITS_PER_MP_LIMB - 1); 
	  else up[0] = 0; 
	  usize++; 
	}
    }

  do
    {
      TMP_MARK (marker);

      err = rsize*BITS_PER_MP_LIMB; 
      if (rsize < usize) { err--; }
      if (err > rrsize * BITS_PER_MP_LIMB) 
	{ err = rrsize * BITS_PER_MP_LIMB; }
      
      tmp = (mp_ptr) TMP_ALLOC (rsize * BYTES_PER_MP_LIMB);  
      rp = (mp_ptr) TMP_ALLOC (rrsize * BYTES_PER_MP_LIMB); 

      if (usize >= rsize) { 
	MPN_COPY (tmp, up + usize - rsize, rsize);
      }
      else { 
	MPN_COPY (tmp + rsize - usize, up, usize);
	MPN_ZERO (tmp, rsize - usize); 
      }

      /* Do the real job */
 
#ifdef DEBUG
      printf("Taking the sqrt of : "); 
      for(k = rsize - 1; k >= 0; k--) { printf("%lu ", tmp[k]); }
      printf(".\n"); 
#endif

      q_limb = mpn_sqrtrem (rp, NULL, tmp, rsize);

#ifdef DEBUG
      printf("The result is : \n"); 
      printf("sqrt : "); 
      for(k = rrsize - 1; k >= 0; k--) { printf("%lu ", rp[k]); }
      printf("(q_limb = %lu)\n", q_limb); 
#endif
      
      can_round = (mpfr_can_round_raw(rp, rrsize, 1, err, 
				      GMP_RNDZ, rnd_mode, PREC(r))); 

      /* If we used all the limbs of both the dividend and the divisor, 
	 then we have the correct RNDZ rounding */

      if (!can_round && (rsize < 2*usize)) 
	{ 
#ifdef DEBUG
	  printf("Increasing the precision.\n"); 
#endif
	  printf("#"); 
	  TMP_FREE(marker); 
	}
    }
  while (!can_round && (rsize < 2*usize) 
	 && (rsize += 2) && (rrsize ++)); 

  if (can_round) 
    {
      cc = mpfr_round_raw(rp, rp, err, 0, PREC(r), rnd_mode);  
      rrsize = (PREC(r) - 1)/BITS_PER_MP_LIMB + 1; 
    }
  else
    /* Use the return value of sqrtrem to decide of the rounding         */
    /* Note that at this point the sqrt has been computed                */
    /* EXACTLY. If rounding = GMP_RNDZ, do nothing [comes from           */
    /* the fact that the exact square root can end with a bunch of ones, */
    /* and in that case we indeed cannot round if we do not know that    */
    /* the computation was exact.                                        */
    switch (rnd_mode)
      {
      case GMP_RNDZ : 
      case GMP_RNDD : break; 

      case GMP_RNDN : 
	/* Not in the situation ...0 111111 */
	rw = (PREC(r) + 1) & (BITS_PER_MP_LIMB - 1);
	if (rw) { rw = BITS_PER_MP_LIMB - rw; nw = 0; } else nw = 1; 
	if ((rp[nw] >> rw) & 1 &&                     /* Not 0111111111 */
	    (q_limb ||                                /* Nonzero remainder */
	    (rw ? (rp[nw] >> (rw + 1)) & 1 : 
	     (rp[nw] >> (BITS_PER_MP_LIMB - 1)) & 1))) /* or even rounding */ 
	  cc = mpn_add_1(rp + nw, rp + nw, rrsize, ((mp_limb_t)1) << rw); 
	break;
 
      case GMP_RNDU : 
	if (q_limb)
	  cc = mpn_add_1(rp, rp, rrsize, 1 << (BITS_PER_MP_LIMB - 
					       (PREC(r) & 
						(BITS_PER_MP_LIMB - 1))));
      }

  if (cc) {
    mpn_rshift(rp, rp, rrsize, 1);
    rp[rrsize-1] |= (mp_limb_t) 1 << (BITS_PER_MP_LIMB-1);
    r->_mp_exp++; 
  }
    

  rrsize = (PREC(r) - 1)/BITS_PER_MP_LIMB + 1;  
  MPN_COPY(r->_mp_d + ABSSIZE(r) - rrsize, rp, rrsize); 

  if (PREC(r) & (BITS_PER_MP_LIMB - 1))
    MANT(r) [ABSSIZE(r) - rrsize] &= ~(((mp_limb_t)1 << (BITS_PER_MP_LIMB - 
				   (PREC(r) & (BITS_PER_MP_LIMB - 1)))) - 1) ; 
  
  TMP_FREE(marker0); TMP_FREE (marker);
}