xref: /reactos/base/applications/calc/utl_mpfr.c (revision 6b82ed57)
1 /*
2  * ReactOS Calc (Utility functions for GMP/MPFR engine)
3  *
4  * Copyright 2007-2017, Carlo Bramini
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "calc.h"
22 
23 void prepare_rpn_result_2(calc_number_t *rpn, TCHAR *buffer, int size, int base)
24 {
25     char   temp[1024];
26     char  *ptr, *dst;
27     int    width, max_ld_width;
28     unsigned long int n, q;
29     mpz_t   zz;
30     mpf_t   ff;
31 
32     mpz_init(zz);
33     mpf_init(ff);
34     mpfr_get_z(zz, rpn->mf, MPFR_DEFAULT_RND);
35     mpfr_get_f(ff, rpn->mf, MPFR_DEFAULT_RND);
36 
37     switch (base) {
38     case IDC_RADIO_HEX:
39         gmp_sprintf(temp, "%ZX", zz);
40         break;
41     case IDC_RADIO_DEC:
42         /*
43          * The output display is much shorter in standard mode,
44          * so I'm forced to reduce the precision here :(
45          */
46         if (calc.layout == CALC_LAYOUT_STANDARD)
47             max_ld_width = 16;
48         else
49             max_ld_width = 64;
50 
51         /* calculate the width of integer number */
52         if (mpf_sgn(ff) == 0)
53             width = 1;
54         else {
55             mpfr_t t;
56             mpfr_init(t);
57             mpfr_abs(t, rpn->mf, MPFR_DEFAULT_RND);
58             mpfr_log10(t, t, MPFR_DEFAULT_RND);
59             width = 1 + mpfr_get_si(t, MPFR_DEFAULT_RND);
60             mpfr_clear(t);
61         }
62         if (calc.sci_out == TRUE || width > max_ld_width || width < -max_ld_width)
63             ptr = temp + gmp_sprintf(temp, "%*.*#Fe", 1, max_ld_width, ff);
64         else {
65             ptr = temp + gmp_sprintf(temp, "%#*.*Ff", width, ((max_ld_width-width-1)>=0) ? max_ld_width-width-1 : 0, ff);
66             dst = strchr(temp, '.');
67             while (--ptr > dst)
68                 if (*ptr != '0')
69                     break;
70 
71             /* put the string terminator for removing the final '0' (if any) */
72             ptr[1] = '\0';
73             /* check if the number finishes with '.' */
74             if (ptr == dst)
75                 /* remove the dot (it will be re-added later) */
76                 ptr[0] = '\0';
77         }
78         break;
79     case IDC_RADIO_OCT:
80         gmp_sprintf(temp, "%Zo", zz);
81         break;
82     case IDC_RADIO_BIN:
83         /* if the number is zero, just write 0 ;) */
84         if (rpn_is_zero(rpn)) {
85             temp[0] = _T('0');
86             temp[1] = _T('\0');
87             break;
88         }
89         /* repeat until a bit set to '1' is found */
90         n = 0;
91         do {
92             q = mpz_scan1(zz, n);
93             if (q == ULONG_MAX)
94                 break;
95             while (n < q)
96                 temp[n++] = '0';
97             temp[n++] = '1';
98         } while (1);
99         /* now revert the string into TCHAR buffer */
100         for (q=0; q<n; q++)
101             buffer[n-q-1] = (temp[q] == '1') ? _T('1') : _T('0');
102         buffer[n] = _T('\0');
103 
104         mpz_clear(zz);
105         mpf_clear(ff);
106         return;
107     }
108     mpz_clear(zz);
109     mpf_clear(ff);
110     StringCchPrintf(buffer, size, _T("%hs"), temp);
111 }
112 
113 void convert_text2number_2(calc_number_t *a)
114 {
115     int base;
116 #ifdef UNICODE
117     int sz;
118     char *temp;
119 #endif
120 
121     switch (calc.base) {
122     case IDC_RADIO_HEX: base = 16; break;
123     case IDC_RADIO_DEC: base = 10; break;
124     case IDC_RADIO_OCT: base = 8; break;
125     case IDC_RADIO_BIN: base = 2; break;
126     default: return;
127     }
128 #ifdef UNICODE
129 /*
130  * libmpfr and libgmp accept only ascii chars.
131  */
132     sz = WideCharToMultiByte(CP_ACP, 0, calc.buffer, -1, NULL, 0, NULL, NULL);
133     if (!sz)
134         return;
135     temp = (char *)_alloca(sz);
136     sz = WideCharToMultiByte(CP_ACP, 0, calc.buffer, -1, temp, sz, NULL, NULL);
137     mpfr_strtofr(a->mf, temp, NULL, base, MPFR_DEFAULT_RND);
138 #else
139     mpfr_strtofr(a->mf, calc.buffer, NULL, base, MPFR_DEFAULT_RND);
140 #endif
141 }
142 
143 void convert_real_integer(unsigned int base)
144 {
145     switch (base) {
146     case IDC_RADIO_DEC:
147         break;
148     case IDC_RADIO_OCT:
149     case IDC_RADIO_BIN:
150     case IDC_RADIO_HEX:
151         if (calc.base == IDC_RADIO_DEC) {
152             mpfr_trunc(calc.code.mf, calc.code.mf);
153             apply_int_mask(&calc.code);
154         }
155         break;
156     }
157 }
158