1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 /*
  26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  27  * Use is subject to license terms.
  28  */
  29 
  30 #pragma weak __remquo = remquo
  31 
  32 /* INDENT OFF */
  33 /*
  34  * double remquo(double x, double y, int *quo) return remainder(x,y) and an
  35  * integer pointer quo such that *quo = N mod {2**31}, where N is the
  36  * exact integral part of x/y rounded to nearest even.
  37  *
  38  * remquo call internal fmodquo
  39  */
  40 /* INDENT ON */
  41 
  42 #include "libm.h"
  43 #include "libm_protos.h"
  44 #include <math.h>         /* fabs() */
  45 #include <sys/isa_defs.h>
  46 
  47 #if defined(_BIG_ENDIAN)
  48 #define HIWORD  0
  49 #define LOWORD  1
  50 #else
  51 #define HIWORD  1
  52 #define LOWORD  0
  53 #endif
  54 #define __HI(x) ((int *) &x)[HIWORD]
  55 #define __LO(x) ((int *) &x)[LOWORD]
  56 
  57 static const double one = 1.0, Zero[] = {0.0, -0.0};
  58 
  59 static double
  60 fmodquo(double x, double y, int *quo) {
  61         int n, hx, hy, hz, ix, iy, sx, sq, i, m;
  62         unsigned lx, ly, lz;
  63 
  64         hx = __HI(x);           /* high word of x */
  65         lx = __LO(x);           /* low  word of x */
  66         hy = __HI(y);           /* high word of y */
  67         ly = __LO(y);           /* low  word of y */
  68         sx = hx & 0x80000000;       /* sign of x */
  69         sq = (hx ^ hy) & 0x80000000;        /* sign of x/y */
  70         hx ^= sx;               /* |x| */
  71         hy &= 0x7fffffff;   /* |y| */
  72 
  73         /* purge off exception values */
  74         *quo = 0;
  75         if ((hy | ly) == 0 || hx >= 0x7ff00000 ||    /* y=0, or x !finite */
  76             (hy | ((ly | -ly) >> 31)) > 0x7ff00000)    /* or y is NaN */
  77                 return ((x * y) / (x * y));
  78         if (hx <= hy) {
  79                 if (hx < hy || lx < ly)
  80                         return (x);     /* |x|<|y| return x */
  81                 if (lx == ly) {
  82                         *quo = 1 + (sq >> 30);
  83                         /* |x|=|y| return x*0 */
  84                         return (Zero[(unsigned) sx >> 31]);
  85                 }
  86         }
  87 
  88         /* determine ix = ilogb(x) */
  89         if (hx < 0x00100000) {       /* subnormal x */
  90                 if (hx == 0) {
  91                         for (ix = -1043, i = lx; i > 0; i <<= 1)
  92                                 ix -= 1;
  93                 } else {
  94                         for (ix = -1022, i = (hx << 11); i > 0; i <<= 1)
  95                                 ix -= 1;
  96                 }
  97         } else
  98                 ix = (hx >> 20) - 1023;
  99 
 100         /* determine iy = ilogb(y) */
 101         if (hy < 0x00100000) {       /* subnormal y */
 102                 if (hy == 0) {
 103                         for (iy = -1043, i = ly; i > 0; i <<= 1)
 104                                 iy -= 1;
 105                 } else {
 106                         for (iy = -1022, i = (hy << 11); i > 0; i <<= 1)
 107                                 iy -= 1;
 108                 }
 109         } else
 110                 iy = (hy >> 20) - 1023;
 111 
 112         /* set up {hx,lx}, {hy,ly} and align y to x */
 113         if (ix >= -1022)
 114                 hx = 0x00100000 | (0x000fffff & hx);
 115         else {                  /* subnormal x, shift x to normal */
 116                 n = -1022 - ix;
 117                 if (n <= 31) {
 118                         hx = (hx << n) | (lx >> (32 - n));
 119                         lx <<= n;
 120                 } else {
 121                         hx = lx << (n - 32);
 122                         lx = 0;
 123                 }
 124         }
 125         if (iy >= -1022)
 126                 hy = 0x00100000 | (0x000fffff & hy);
 127         else {                  /* subnormal y, shift y to normal */
 128                 n = -1022 - iy;
 129                 if (n <= 31) {
 130                         hy = (hy << n) | (ly >> (32 - n));
 131                         ly <<= n;
 132                 } else {
 133                         hy = ly << (n - 32);
 134                         ly = 0;
 135                 }
 136         }
 137 
 138         /* fix point fmod */
 139         n = ix - iy;
 140         m = 0;
 141         while (n--) {
 142                 hz = hx - hy;
 143                 lz = lx - ly;
 144                 if (lx < ly)
 145                         hz -= 1;
 146                 if (hz < 0) {
 147                         hx = hx + hx + (lx >> 31);
 148                         lx = lx + lx;
 149                 } else {
 150                         m += 1;
 151                         if ((hz | lz) == 0) {   /* return sign(x)*0 */
 152                                 if (n < 31)
 153                                         m <<= 1 + n;
 154                                 else
 155                                         m = 0;
 156                                 m &= 0x7fffffff;
 157                                 *quo = sq >= 0 ? m : -m;
 158                                 return (Zero[(unsigned) sx >> 31]);
 159                         }
 160                         hx = hz + hz + (lz >> 31);
 161                         lx = lz + lz;
 162                 }
 163                 m += m;
 164         }
 165         hz = hx - hy;
 166         lz = lx - ly;
 167         if (lx < ly)
 168                 hz -= 1;
 169         if (hz >= 0) {
 170                 hx = hz;
 171                 lx = lz;
 172                 m += 1;
 173         }
 174         m &= 0x7fffffff;
 175         *quo = sq >= 0 ? m : -m;
 176 
 177         /* convert back to floating value and restore the sign */
 178         if ((hx | lx) == 0) {   /* return sign(x)*0 */
 179                 return (Zero[(unsigned) sx >> 31]);
 180         }
 181         while (hx < 0x00100000) {    /* normalize x */
 182                 hx = hx + hx + (lx >> 31);
 183                 lx = lx + lx;
 184                 iy -= 1;
 185         }
 186         if (iy >= -1022) {   /* normalize output */
 187                 hx = (hx - 0x00100000) | ((iy + 1023) << 20);
 188                 __HI(x) = hx | sx;
 189                 __LO(x) = lx;
 190         } else {                        /* subnormal output */
 191                 n = -1022 - iy;
 192                 if (n <= 20) {
 193                         lx = (lx >> n) | ((unsigned) hx << (32 - n));
 194                         hx >>= n;
 195                 } else if (n <= 31) {
 196                         lx = (hx << (32 - n)) | (lx >> n);
 197                         hx = sx;
 198                 } else {
 199                         lx = hx >> (n - 32);
 200                         hx = sx;
 201                 }
 202                 __HI(x) = hx | sx;
 203                 __LO(x) = lx;
 204                 x *= one;       /* create necessary signal */
 205         }
 206         return (x);             /* exact output */
 207 }
 208 
 209 #define zero    Zero[0]
 210 
 211 double
 212 remquo(double x, double y, int *quo) {
 213         int hx, hy, sx, sq;
 214         double v;
 215         unsigned ly;
 216 
 217         hx = __HI(x);           /* high word of x */
 218         hy = __HI(y);           /* high word of y */
 219         ly = __LO(y);           /* low  word of y */
 220         sx = hx & 0x80000000;       /* sign of x */
 221         sq = (hx ^ hy) & 0x80000000;        /* sign of x/y */
 222         hx ^= sx;               /* |x| */
 223         hy &= 0x7fffffff;   /* |y| */
 224 
 225         /* purge off exception values */
 226         *quo = 0;
 227         if ((hy | ly) == 0 || hx >= 0x7ff00000 ||    /* y=0, or x !finite */
 228             (hy | ((ly | -ly) >> 31)) > 0x7ff00000)    /* or y is NaN */
 229                 return ((x * y) / (x * y));
 230 
 231         y = fabs(y);
 232         x = fabs(x);
 233         if (hy <= 0x7fdfffff) {
 234                 x = fmodquo(x, y + y, quo);
 235                 *quo = ((*quo) & 0x3fffffff) << 1;
 236         }
 237         if (hy < 0x00200000) {
 238                 if (x + x > y) {
 239                         *quo += 1;
 240                         if (x == y)
 241                                 x = zero;
 242                         else
 243                                 x -= y;
 244                         if (x + x >= y) {
 245                                 x -= y;
 246                                 *quo += 1;
 247                         }
 248                 }
 249         } else {
 250                 v = 0.5 * y;
 251                 if (x > v) {
 252                         *quo += 1;
 253                         if (x == y)
 254                                 x = zero;
 255                         else
 256                                 x -= y;
 257                         if (x >= v) {
 258                                 x -= y;
 259                                 *quo += 1;
 260                         }
 261                 }
 262         }
 263         if (sq != 0)
 264                 *quo = -(*quo);
 265         return (sx == 0 ? x : -x);
 266 }