xref: /netbsd-src/external/lgpl3/gmp/dist/mpf/set_q.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
1 /* mpf_set_q (mpf_t rop, mpq_t op) -- Convert the rational op to the float rop.
2 
3 Copyright 1996, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
4 
5 This file is part of the GNU MP Library.
6 
7 The GNU MP Library is free software; you can redistribute it and/or modify
8 it under the terms of either:
9 
10   * the GNU Lesser General Public License as published by the Free
11     Software Foundation; either version 3 of the License, or (at your
12     option) any later version.
13 
14 or
15 
16   * the GNU General Public License as published by the Free Software
17     Foundation; either version 2 of the License, or (at your option) any
18     later version.
19 
20 or both in parallel, as here.
21 
22 The GNU MP Library is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 for more details.
26 
27 You should have received copies of the GNU General Public License and the
28 GNU Lesser General Public License along with the GNU MP Library.  If not,
29 see https://www.gnu.org/licenses/.  */
30 
31 #include <stdio.h>  /* for NULL */
32 #include "gmp.h"
33 #include "gmp-impl.h"
34 #include "longlong.h"
35 
36 
37 /* As usual the aim is to produce PREC(r) limbs, with the high non-zero.
38    The basic mpn_tdiv_qr produces a quotient of nsize-dsize+1 limbs, with
39    either the high or second highest limb non-zero.  We arrange for
40    nsize-dsize+1 to equal prec+1, hence giving either prec or prec+1 result
41    limbs at PTR(r).
42 
43    nsize-dsize+1 == prec+1 is achieved by adjusting num(q), either dropping
44    low limbs if it's too big, or padding with low zeros if it's too small.
45    The full given den(q) is always used.
46 
47    We cannot truncate den(q), because even when it's much bigger than prec
48    the last limbs can still influence the final quotient.  Often they don't,
49    but we leave optimization of that to a prospective quotient-only mpn
50    division.
51 
52    Not done:
53 
54    If den(q) is a power of 2 then we may end up with low zero limbs on the
55    result.  But nothing is done about this, since it should be unlikely on
56    random data, and can be left to an application to call mpf_div_2exp if it
57    might occur with any frequency.
58 
59    Enhancements:
60 
61    The high quotient limb is non-zero when high{np,dsize} >= {dp,dsize}.  We
62    could make that comparison and use qsize==prec instead of qsize==prec+1,
63    to save one limb in the division.
64 
65    Future:
66 
67    If/when mpn_tdiv_qr supports its qxn parameter we can use that instead of
68    padding n with zeros in temporary space.
69 
70    If/when a quotient-only division exists it can be used here immediately.
71    remp is only to satisfy mpn_tdiv_qr, the remainder is not used.  */
72 
73 void
74 mpf_set_q (mpf_t r, mpq_srcptr q)
75 {
76   mp_srcptr np, dp;
77   mp_size_t prec, nsize, dsize, qsize, prospective_qsize, tsize, zeros;
78   mp_size_t sign_quotient, high_zero;
79   mp_ptr qp, tp, remp;
80   mp_exp_t exp;
81   TMP_DECL;
82 
83   ASSERT (SIZ(&q->_mp_den) > 0);  /* canonical q */
84 
85   nsize = SIZ (&q->_mp_num);
86   dsize = SIZ (&q->_mp_den);
87 
88   if (UNLIKELY (nsize == 0))
89     {
90       SIZ (r) = 0;
91       EXP (r) = 0;
92       return;
93     }
94 
95   TMP_MARK;
96 
97   prec = PREC (r);
98   qp = PTR (r);
99 
100   sign_quotient = nsize;
101   nsize = ABS (nsize);
102   np = PTR (&q->_mp_num);
103   dp = PTR (&q->_mp_den);
104 
105   prospective_qsize = nsize - dsize + 1;  /* q from using given n,d sizes */
106   exp = prospective_qsize;                /* ie. number of integer limbs */
107   qsize = prec + 1;                       /* desired q */
108 
109   zeros = qsize - prospective_qsize;   /* n zeros to get desired qsize */
110   tsize = nsize + zeros;               /* possible copy of n */
111 
112   if (WANT_TMP_DEBUG)
113     {
114       /* separate alloc blocks, for malloc debugging */
115       remp = TMP_ALLOC_LIMBS (dsize);
116       tp = NULL;
117       if (zeros > 0)
118         tp = TMP_ALLOC_LIMBS (tsize);
119     }
120   else
121     {
122       /* one alloc with a conditionalized size, for efficiency */
123       mp_size_t size = dsize + (zeros > 0 ? tsize : 0);
124       remp = TMP_ALLOC_LIMBS (size);
125       tp = remp + dsize;
126     }
127 
128   if (zeros > 0)
129     {
130       /* pad n with zeros into temporary space */
131       MPN_ZERO (tp, zeros);
132       MPN_COPY (tp+zeros, np, nsize);
133       np = tp;
134       nsize = tsize;
135     }
136   else
137     {
138       /* shorten n to get desired qsize */
139       nsize += zeros;
140       np -= zeros;
141     }
142 
143   ASSERT (nsize-dsize+1 == qsize);
144   mpn_tdiv_qr (qp, remp, (mp_size_t) 0, np, nsize, dp, dsize);
145 
146   /* strip possible zero high limb */
147   high_zero = (qp[qsize-1] == 0);
148   qsize -= high_zero;
149   exp -= high_zero;
150 
151   EXP (r) = exp;
152   SIZ (r) = sign_quotient >= 0 ? qsize : -qsize;
153 
154   TMP_FREE;
155 }
156