1/* mpq_set_d(mpq_t q, double d) -- Set q to d without rounding.
2
3Copyright 2000, 2002, 2003 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 the GNU Lesser General Public License as published by
9the Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
11
12The GNU MP Library is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15License for more details.
16
17You should have received a copy of the GNU Lesser General Public License
18along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
19
20#include "config.h"
21
22#if HAVE_FLOAT_H
23#include <float.h>  /* for DBL_MAX */
24#endif
25
26#include "gmp.h"
27#include "gmp-impl.h"
28#include "longlong.h"
29
30#if LIMBS_PER_DOUBLE > 4
31  choke me
32#endif
33
34void
35mpq_set_d (mpq_ptr dest, double d)
36{
37  int negative;
38  mp_exp_t exp;
39  mp_limb_t tp[LIMBS_PER_DOUBLE];
40  mp_ptr np, dp;
41  mp_size_t nn, dn;
42  int c;
43
44  DOUBLE_NAN_INF_ACTION (d,
45                         __gmp_invalid_operation (),
46                         __gmp_invalid_operation ());
47
48  negative = d < 0;
49  d = ABS (d);
50
51  exp = __gmp_extract_double (tp, d);
52
53  /* There are two main version of the conversion.  The `then' arm handles
54     numbers with a fractional part, while the `else' arm handles integers.  */
55#if LIMBS_PER_DOUBLE == 4
56  if (exp <= 1 || (exp == 2 && (tp[0] | tp[1]) != 0))
57#endif
58#if LIMBS_PER_DOUBLE == 3
59  if (exp <= 1 || (exp == 2 && tp[0] != 0))
60#endif
61#if LIMBS_PER_DOUBLE == 2
62  if (exp <= 1)
63#endif
64    {
65      if (d == 0.0)
66	{
67	  SIZ(&(dest->_mp_num)) = 0;
68	  SIZ(&(dest->_mp_den)) = 1;
69	  PTR(&(dest->_mp_den))[0] = 1;
70	  return;
71	}
72
73      dn = -exp;
74      MPZ_REALLOC (&(dest->_mp_num), 3);
75      np = PTR(&(dest->_mp_num));
76#if LIMBS_PER_DOUBLE == 4
77      if ((tp[0] | tp[1] | tp[2]) == 0)
78	np[0] = tp[3], nn = 1;
79      else if ((tp[0] | tp[1]) == 0)
80	np[1] = tp[3], np[0] = tp[2], nn = 2;
81      else if (tp[0] == 0)
82	np[2] = tp[3], np[1] = tp[2], np[0] = tp[1], nn = 3;
83      else
84	np[3] = tp[3], np[2] = tp[2], np[1] = tp[1], np[0] = tp[0], nn = 4;
85#endif
86#if LIMBS_PER_DOUBLE == 3
87      if ((tp[0] | tp[1]) == 0)
88	np[0] = tp[2], nn = 1;
89      else if (tp[0] == 0)
90	np[1] = tp[2], np[0] = tp[1], nn = 2;
91      else
92	np[2] = tp[2], np[1] = tp[1], np[0] = tp[0], nn = 3;
93#endif
94#if LIMBS_PER_DOUBLE == 2
95      if (tp[0] == 0)
96	np[0] = tp[1], nn = 1;
97      else
98	np[1] = tp[1], np[0] = tp[0], nn = 2;
99#endif
100      dn += nn + 1;
101      ASSERT_ALWAYS (dn > 0);
102      MPZ_REALLOC (&(dest->_mp_den), dn);
103      dp = PTR(&(dest->_mp_den));
104      MPN_ZERO (dp, dn - 1);
105      dp[dn - 1] = 1;
106      count_trailing_zeros (c, np[0] | dp[0]);
107      if (c != 0)
108	{
109	  mpn_rshift (np, np, nn, c);
110	  nn -= np[nn - 1] == 0;
111	  mpn_rshift (dp, dp, dn, c);
112	  dn -= dp[dn - 1] == 0;
113	}
114      SIZ(&(dest->_mp_den)) = dn;
115      SIZ(&(dest->_mp_num)) = negative ? -nn : nn;
116    }
117  else
118    {
119      nn = exp;
120      MPZ_REALLOC (&(dest->_mp_num), nn);
121      np = PTR(&(dest->_mp_num));
122      switch (nn)
123        {
124	default:
125	  MPN_ZERO (np, nn - LIMBS_PER_DOUBLE);
126	  np += nn - LIMBS_PER_DOUBLE;
127	  /* fall through */
128#if LIMBS_PER_DOUBLE == 2
129	case 2:
130	  np[1] = tp[1], np[0] = tp[0];
131	  break;
132#endif
133#if LIMBS_PER_DOUBLE == 3
134	case 3:
135	  np[2] = tp[2], np[1] = tp[1], np[0] = tp[0];
136	  break;
137	case 2:
138	  np[1] = tp[2], np[0] = tp[1];
139	  break;
140#endif
141#if LIMBS_PER_DOUBLE == 4
142	case 4:
143	  np[3] = tp[3], np[2] = tp[2], np[1] = tp[1], np[0] = tp[0];
144	  break;
145	case 3:
146	  np[2] = tp[3], np[1] = tp[2], np[0] = tp[1];
147	  break;
148	case 2:
149	  np[1] = tp[3], np[0] = tp[2];
150	  break;
151#endif
152	}
153      dp = PTR(&(dest->_mp_den));
154      dp[0] = 1;
155      SIZ(&(dest->_mp_den)) = 1;
156      SIZ(&(dest->_mp_num)) = negative ? -nn : nn;
157    }
158}
159