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 /*
  27  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  28  * Use is subject to license terms.
  29  */
  30 
  31 #pragma weak __fmod = fmod
  32 
  33 #include "libm.h"
  34 
  35 static const double zero = 0.0;
  36 
  37 /*
  38  * The following implementation assumes fast 64-bit integer arith-
  39  * metic.  This is fine for sparc because we build libm in v8plus
  40  * mode.  It's also fine for sparcv9 and amd64, although we have
  41  * assembly code on amd64.  For x86, it would be better to use
  42  * 32-bit code, but we have assembly for x86, too.
  43  */
  44 double
  45 fmod(double x, double y)
  46 {
  47         double w;
  48         long long hx, ix, iy, iz;
  49         int nd, k, ny;
  50 
  51         hx = *(long long *)&x;
  52         ix = hx & ~0x8000000000000000ull;
  53         iy = *(long long *)&y & ~0x8000000000000000ull;
  54 
  55         /* handle special cases */
  56         if (iy == 0ll)
  57                 return (_SVID_libm_err(x, y, 27));
  58 
  59         if (ix >= 0x7ff0000000000000ll || iy > 0x7ff0000000000000ll)
  60                 return ((x * y) * zero);
  61 
  62         if (ix <= iy)
  63                 return ((ix < iy) ? x : x *zero);
  64 
  65         /*
  66          * Set:
  67          *      ny = true exponent of y
  68          *      nd = true exponent of x minus true exponent of y
  69          *      ix = normalized significand of x
  70          *      iy = normalized significand of y
  71          */
  72         ny = iy >> 52;
  73         k = ix >> 52;
  74 
  75         if (ny == 0) {
  76                 /* y is subnormal, x could be normal or subnormal */
  77                 ny = 1;
  78 
  79                 while (iy < 0x0010000000000000ll) {
  80                         ny -= 1;
  81                         iy += iy;
  82                 }
  83 
  84                 nd = k - ny;
  85 
  86                 if (k == 0) {
  87                         nd += 1;
  88 
  89                         while (ix < 0x0010000000000000ll) {
  90                                 nd -= 1;
  91                                 ix += ix;
  92                         }
  93                 } else {
  94                         ix = 0x0010000000000000ll | (ix & 0x000fffffffffffffll);
  95                 }
  96         } else {
  97                 /* both x and y are normal */
  98                 nd = k - ny;
  99                 ix = 0x0010000000000000ll | (ix & 0x000fffffffffffffll);
 100                 iy = 0x0010000000000000ll | (iy & 0x000fffffffffffffll);
 101         }
 102 
 103         /* perform fixed point mod */
 104         while (nd--) {
 105                 iz = ix - iy;
 106 
 107                 if (iz >= 0)
 108                         ix = iz;
 109 
 110                 ix += ix;
 111         }
 112 
 113         iz = ix - iy;
 114 
 115         if (iz >= 0)
 116                 ix = iz;
 117 
 118         /* convert back to floating point and restore the sign */
 119         if (ix == 0ll)
 120                 return (x * zero);
 121 
 122         while (ix < 0x0010000000000000ll) {
 123                 ix += ix;
 124                 ny -= 1;
 125         }
 126 
 127         while (ix > 0x0020000000000000ll) {  /* XXX can this ever happen? */
 128                 ny += 1;
 129                 ix >>= 1;
 130         }
 131 
 132         if (ny <= 0) {
 133                 /* result is subnormal */
 134                 k = -ny + 1;
 135                 ix >>= k;
 136                 *(long long *) &w = (hx & 0x8000000000000000ull) | ix;
 137                 return (w);
 138         }
 139 
 140         *(long long *) &w = (hx & 0x8000000000000000ull) | ((long long)ny <<
 141             52) | (ix & 0x000fffffffffffffll);
 142         return (w);
 143 }