summaryrefslogtreecommitdiff
path: root/mpf/ui_div.c
blob: e09de139319e0fe98e03ce63a8e63471cfabb742 (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
/* mpf_ui_div -- Divide an unsigned integer with a float.

Copyright 1993-1996, 2000-2002, 2004, 2005, 2012 Free Software Foundation, Inc.

This file is part of the GNU MP Library.

The GNU MP Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or (at
your option) any later version.

The GNU MP Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with the GNU MP Library.  If not, see https://www.gnu.org/licenses/.  */

#include <stdio.h>  /* for NULL */
#include "gmp.h"
#include "gmp-impl.h"
#include "longlong.h"


void
mpf_ui_div (mpf_ptr r, unsigned long int u, mpf_srcptr v)
{
  mp_srcptr vp;
  mp_ptr rp, tp, remp, new_vp;
  mp_size_t vsize;
  mp_size_t rsize, prospective_rsize, zeros, tsize, high_zero;
  mp_size_t sign_quotient;
  mp_size_t prec;
  mp_exp_t rexp;
  TMP_DECL;

  vsize = v->_mp_size;
  sign_quotient = vsize;

  if (UNLIKELY (vsize == 0))
    DIVIDE_BY_ZERO;

  if (UNLIKELY (u == 0))
    {
      r->_mp_size = 0;
      r->_mp_exp = 0;
      return;
    }

  vsize = ABS (vsize);
  prec = r->_mp_prec;

  TMP_MARK;
  rexp = 1 - v->_mp_exp + 1;

  rp = r->_mp_d;
  vp = v->_mp_d;

  prospective_rsize = 1 - vsize + 1;    /* quot from using given u,v sizes */
  rsize = prec + 1;                     /* desired quot size */

  zeros = rsize - prospective_rsize;    /* padding u to give rsize */
  tsize = 1 + zeros;                    /* u with zeros */

  if (WANT_TMP_DEBUG)
    {
      /* separate alloc blocks, for malloc debugging */
      remp = TMP_ALLOC_LIMBS (vsize);
      tp = TMP_ALLOC_LIMBS (tsize);
      new_vp = NULL;
      if (rp == vp)
        new_vp = TMP_ALLOC_LIMBS (vsize);
    }
  else
    {
      /* one alloc with calculated size, for efficiency */
      mp_size_t size = vsize + tsize + (rp == vp ? vsize : 0);
      remp = TMP_ALLOC_LIMBS (size);
      tp = remp + vsize;
      new_vp = tp + tsize;
    }

  /* ensure divisor doesn't overlap quotient */
  if (rp == vp)
    {
      MPN_COPY (new_vp, vp, vsize);
      vp = new_vp;
    }

  MPN_ZERO (tp, tsize-1);

  tp[tsize - 1] = u & GMP_NUMB_MASK;
#if BITS_PER_ULONG > GMP_NUMB_BITS
  if (u > GMP_NUMB_MAX)
    {
      /* tsize-vsize+1 == rsize, so tsize >= rsize.  rsize == prec+1 >= 2,
         so tsize >= 2, hence there's room for 2-limb u with nails */
      ASSERT (tsize >= 2);
      tp[tsize - 1] = u >> GMP_NUMB_BITS;
      tp[tsize - 2] = u & GMP_NUMB_MASK;
      rexp++;
    }
#endif

  ASSERT (tsize-vsize+1 == rsize);
  mpn_tdiv_qr (rp, remp, (mp_size_t) 0, tp, tsize, vp, vsize);

  /* strip possible zero high limb */
  high_zero = (rp[rsize-1] == 0);
  rsize -= high_zero;
  rexp -= high_zero;

  r->_mp_size = sign_quotient >= 0 ? rsize : -rsize;
  r->_mp_exp = rexp;
  TMP_FREE;
}