blob: 4d9779d85f18c722e0917bac255ef89c2779831d [file] [log] [blame]
Austin Schuhbb1338c2024-06-15 19:31:16 -07001/* double mpq_get_d (mpq_t src) -- mpq to double, rounding towards zero.
2
3Copyright 1995, 1996, 2001-2005, 2018, 2019 Free Software Foundation, Inc.
4
5This file is part of the GNU MP Library.
6
7The GNU MP Library is free software; you can redistribute it and/or modify
8it 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
14or
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
20or both in parallel, as here.
21
22The GNU MP Library is distributed in the hope that it will be useful, but
23WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25for more details.
26
27You should have received copies of the GNU General Public License and the
28GNU Lesser General Public License along with the GNU MP Library. If not,
29see https://www.gnu.org/licenses/. */
30
31#include <stdio.h> /* for NULL */
32#include "gmp-impl.h"
33#include "longlong.h"
34
35
36/* All that's needed is to get the high 53 bits of the quotient num/den,
37 rounded towards zero. More than 53 bits is fine, any excess is ignored
38 by mpn_get_d.
39
40 N_QLIMBS is how many quotient limbs we need to satisfy the mantissa of a
41 double, assuming the highest of those limbs is non-zero. The target
42 qsize for mpn_tdiv_qr is then 1 more than this, since that function may
43 give a zero in the high limb (and non-zero in the second highest).
44
45 The use of 8*sizeof(double) in N_QLIMBS is an overestimate of the
46 mantissa bits, but it gets the same result as the true value (53 or 48 or
47 whatever) when rounded up to a multiple of GMP_NUMB_BITS, for non-nails.
48
49 Enhancements:
50
51 Use the true mantissa size in the N_QLIMBS formula, to save a divide step
52 in nails.
53
54 Examine the high limbs of num and den to see if the highest 1 bit of the
55 quotient will fall high enough that just N_QLIMBS-1 limbs is enough to
56 get the necessary bits, thereby saving a division step.
57
58 Bit shift either num or den to arrange for the above condition on the
59 high 1 bit of the quotient, to save a division step always. A shift to
60 save a division step is definitely worthwhile with mpn_tdiv_qr, though we
61 may want to reassess this on big num/den when a quotient-only division
62 exists.
63
64 Maybe we could estimate the final exponent using nsize-dsize (and
65 possibly the high limbs of num and den), so as to detect overflow and
66 return infinity or zero quickly. Overflow is never very helpful to an
67 application, and can therefore probably be regarded as abnormal, but we
68 may still like to optimize it if the conditions are easy. (This would
69 only be for float formats we know, unknown formats are not important and
70 can be left to mpn_get_d.)
71
72 Future:
73
74 If/when mpn_tdiv_qr supports its qxn parameter we can use that instead of
75 padding n with zeros in temporary space.
76
77 Alternatives:
78
79 An alternative algorithm, that may be faster:
80 0. Let n be somewhat larger than the number of significant bits in a double.
81 1. Extract the most significant n bits of the denominator, and an equal
82 number of bits from the numerator.
83 2. Interpret the extracted numbers as integers, call them a and b
84 respectively, and develop n bits of the fractions ((a + 1) / b) and
85 (a / (b + 1)) using mpn_divrem.
86 3. If the computed values are identical UP TO THE POSITION WE CARE ABOUT,
87 we are done. If they are different, repeat the algorithm from step 1,
88 but first let n = n * 2.
89 4. If we end up using all bits from the numerator and denominator, fall
90 back to a plain division.
91 5. Just to make life harder, The computation of a + 1 and b + 1 above
92 might give carry-out... Needs special handling. It might work to
93 subtract 1 in both cases instead.
94
95 Not certain if this approach would be faster than a quotient-only
96 division. Presumably such optimizations are the sort of thing we would
97 like to have helping everywhere that uses a quotient-only division. */
98
99double
100mpq_get_d (mpq_srcptr src)
101{
102 double res;
103 mp_srcptr np, dp;
104 mp_ptr temp;
105 mp_size_t nsize = SIZ(NUM(src));
106 mp_size_t dsize = SIZ(DEN(src));
107 mp_size_t qsize, prospective_qsize, zeros;
108 mp_size_t sign_quotient = nsize;
109 long exp;
110#define N_QLIMBS (1 + (sizeof (double) + GMP_LIMB_BYTES-1) / GMP_LIMB_BYTES)
111 mp_limb_t qarr[N_QLIMBS + 1];
112 mp_ptr qp = qarr;
113 TMP_DECL;
114
115 ASSERT (dsize > 0); /* canonical src */
116
117 /* mpn_get_d below requires a non-zero operand */
118 if (UNLIKELY (nsize == 0))
119 return 0.0;
120
121 TMP_MARK;
122 nsize = ABS (nsize);
123 dsize = ABS (dsize);
124 np = PTR(NUM(src));
125 dp = PTR(DEN(src));
126
127 prospective_qsize = nsize - dsize; /* from using given n,d */
128 qsize = N_QLIMBS; /* desired qsize */
129
130 zeros = qsize - prospective_qsize; /* padding n to get qsize */
131 exp = (long) -zeros * GMP_NUMB_BITS; /* relative to low of qp */
132
133 /* zero extend n into temporary space, if necessary */
134 if (zeros > 0)
135 {
136 mp_size_t tsize;
137 tsize = nsize + zeros; /* size for copy of n */
138
139 temp = TMP_ALLOC_LIMBS (tsize + 1);
140 MPN_FILL (temp, zeros, 0);
141 MPN_COPY (temp + zeros, np, nsize);
142 np = temp;
143 nsize = tsize;
144 }
145 else /* negative zeros means shorten n */
146 {
147 np -= zeros;
148 nsize += zeros;
149
150 temp = TMP_ALLOC_LIMBS (nsize + 1);
151 }
152
153 ASSERT (qsize == nsize - dsize);
154 mpn_div_q (qp, np, nsize, dp, dsize, temp);
155
156 /* strip possible zero high limb */
157 qsize += (qp[qsize] != 0);
158
159 res = mpn_get_d (qp, qsize, sign_quotient, exp);
160 TMP_FREE;
161 return res;
162}