Home | History | Annotate | Line # | Download | only in src
      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
     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
     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 *
     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 *
     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 *
    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 *
    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