xref: /netbsd-src/external/lgpl3/mpc/dist/src/get_x.c (revision 367b82799ab709709d3c3b541df56a2a14644d3e)
1 /* mpc_get_dc, mpc_get_ldc -- Transform mpc number into C complex number
2    mpc_get_str -- Convert a complex number into a string.
3 
4 Copyright (C) 2009, 2010, 2011, 2020, 2022 INRIA
5 
6 This file is part of GNU MPC.
7 
8 GNU MPC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU Lesser General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16 more details.
17 
18 You should have received a copy of the GNU Lesser General Public License
19 along with this program. If not, see http://www.gnu.org/licenses/ .
20 */
21 
22 #include "config.h"
23 
24 #ifdef HAVE_COMPLEX_H
25 #include <complex.h>
26 #endif
27 
28 #include <locale.h>
29 #include <stdio.h> /* for sprintf, fprintf */
30 #include <ctype.h>
31 #include <string.h>
32 #include "mpc-impl.h"
33 
34 #ifdef HAVE_COMPLEX_H
35 double _Complex
mpc_get_dc(mpc_srcptr op,mpc_rnd_t rnd)36 mpc_get_dc (mpc_srcptr op, mpc_rnd_t rnd) {
37    return I * mpfr_get_d (mpc_imagref (op), MPC_RND_IM (rnd))
38           + mpfr_get_d (mpc_realref (op), MPC_RND_RE (rnd));
39 }
40 
41 long double _Complex
mpc_get_ldc(mpc_srcptr op,mpc_rnd_t rnd)42 mpc_get_ldc (mpc_srcptr op, mpc_rnd_t rnd) {
43    return I * mpfr_get_ld (mpc_imagref (op), MPC_RND_IM (rnd))
44           + mpfr_get_ld (mpc_realref (op), MPC_RND_RE (rnd));
45 }
46 #endif
47 
48 
49 /* Code for mpc_get_str. The output format is "(real imag)", the decimal point
50    of the locale is used. */
51 
52 /* mpfr_prec_t can be either int or long int */
53 #if (_MPFR_EXP_FORMAT == 2)
54 #define MPC_EXP_FORMAT_SPEC "i"
55 #elif (_MPFR_EXP_FORMAT == 3)
56 #define MPC_EXP_FORMAT_SPEC "li"
57 #else
58 #error "value of _MPFR_EXP_FORMAT not supported"
59 #endif
60 
61 static char *
pretty_zero(mpfr_srcptr zero)62 pretty_zero (mpfr_srcptr zero)
63 {
64   char *pretty;
65 
66   pretty = mpc_alloc_str (3);
67 
68   pretty[0] = mpfr_signbit (zero) ? '-' : '+';
69   pretty[1] = '0';
70   pretty[2] = '\0';
71 
72   return pretty;
73 }
74 
75 static char *
prettify(const char * str,const mpfr_exp_t expo,int base,int special)76 prettify (const char *str, const mpfr_exp_t expo, int base, int special)
77 {
78   size_t sz;
79   char *pretty;
80   char *p;
81   const char *s;
82   mpfr_exp_t x;
83   int sign;
84 
85   sz = strlen (str) + 1; /* + terminal '\0' */
86 
87   if (special)
88     {
89       /* special number: nan or inf */
90       pretty = mpc_alloc_str (sz);
91       strcpy (pretty, str);
92 
93       return pretty;
94     }
95 
96   /* regular number */
97 
98   sign = (str[0] == '-' || str[0] == '+');
99 
100   x = expo - 1; /* expo is the exponent value with decimal point BEFORE
101                    the first digit, we want decimal point AFTER the first
102                    digit */
103   if (base == 16)
104     x *= 4; /* the output exponent is a binary exponent */
105 
106   ++sz; /* + decimal point */
107 
108   if (x != 0)
109     {
110       /* augment sz with the size needed for an exponent written in base
111          ten */
112       mpfr_exp_t xx;
113 
114       sz += 3; /* + exponent char + sign + 1 digit */
115 
116       if (x < 0)
117         {
118           /* avoid overflow when changing sign (assuming that, for the
119              mpfr_exp_t type, (max value) is greater than (- min value / 10)) */
120           if (x < -10)
121             {
122               xx = - (x / 10);
123               sz++;
124             }
125           else
126             xx = -x;
127         }
128       else
129         xx = x;
130 
131       /* compute sz += floor(log(expo)/log(10)) without using libm
132          functions */
133       while (xx > 9)
134         {
135           sz++;
136           xx /= 10;
137         }
138     }
139 
140   pretty = mpc_alloc_str (sz);
141   p = pretty;
142 
143   /* 1. optional sign plus first digit */
144   s = str;
145   *p++ = *s++;
146   if (sign)
147     *p++ = *s++;
148 
149   /* 2. decimal point */
150 #ifdef HAVE_LOCALECONV
151   *p++ = *localeconv ()->decimal_point;
152 #else
153   *p++ = '.';
154 #endif
155   *p = '\0';
156 
157   /* 3. other significant digits */
158   strcat (pretty, s);
159 
160   /* 4. exponent (in base ten) */
161   if (x == 0)
162     return pretty;
163 
164   p = pretty + strlen (str) + 1;
165 
166   switch (base)
167     {
168     case 10:
169       *p++ = 'e';
170       break;
171     case 2:
172     case 16:
173       *p++ = 'p';
174       break;
175     default:
176       *p++ = '@';
177     }
178 
179   *p = '\0';
180 
181   sprintf (p, "%+" MPC_EXP_FORMAT_SPEC, x);
182 
183   return pretty;
184 }
185 
186 static char *
get_pretty_str(const int base,const size_t n,mpfr_srcptr x,mpfr_rnd_t rnd)187 get_pretty_str (const int base, const size_t n, mpfr_srcptr x, mpfr_rnd_t rnd)
188 {
189   mpfr_exp_t expo;
190   char *ugly;
191   char *pretty;
192 
193   if (mpfr_zero_p (x))
194     return pretty_zero (x);
195 
196   ugly = mpfr_get_str (NULL, &expo, base, n, x, rnd);
197   MPC_ASSERT (ugly != NULL);
198   pretty = prettify (ugly, expo, base, !mpfr_number_p (x));
199   mpfr_free_str (ugly);
200 
201   return pretty;
202 }
203 
204 char *
mpc_get_str(int base,size_t n,mpc_srcptr op,mpc_rnd_t rnd)205 mpc_get_str (int base, size_t n, mpc_srcptr op, mpc_rnd_t rnd)
206 {
207   size_t needed_size;
208   char *real_str;
209   char *imag_str;
210   char *complex_str = NULL;
211 
212   if (base < 2 || base > 36)
213     return NULL;
214 
215   real_str = get_pretty_str (base, n, mpc_realref (op), MPC_RND_RE (rnd));
216   imag_str = get_pretty_str (base, n, mpc_imagref (op), MPC_RND_IM (rnd));
217 
218   needed_size = strlen (real_str) + strlen (imag_str) + 4;
219 
220   complex_str = mpc_alloc_str (needed_size);
221 MPC_ASSERT (complex_str != NULL);
222 
223   strcpy (complex_str, "(");
224   strcat (complex_str, real_str);
225   strcat (complex_str, " ");
226   strcat (complex_str, imag_str);
227   strcat (complex_str, ")");
228 
229   mpc_free_str (real_str);
230   mpc_free_str (imag_str);
231 
232   return complex_str;
233 }
234