source: EcnlProtoTool/trunk/mruby-2.1.1/src/fmt_fp.c@ 439

Last change on this file since 439 was 439, checked in by coas-nagasima, 4 years ago

mrubyを2.1.1に更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 9.3 KB
Line 
1#ifndef MRB_WITHOUT_FLOAT
2#if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
3/*
4
5Most code in this file originates from musl (src/stdio/vfprintf.c)
6which, just like mruby itself, is licensed under the MIT license.
7
8Copyright (c) 2005-2014 Rich Felker, et al.
9
10Permission is hereby granted, free of charge, to any person obtaining
11a copy of this software and associated documentation files (the
12"Software"), to deal in the Software without restriction, including
13without limitation the rights to use, copy, modify, merge, publish,
14distribute, sublicense, and/or sell copies of the Software, and to
15permit persons to whom the Software is furnished to do so, subject to
16the following conditions:
17
18The above copyright notice and this permission notice shall be
19included in all copies or substantial portions of the Software.
20
21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29*/
30
31#include <limits.h>
32#include <string.h>
33#include <math.h>
34#include <float.h>
35#include <ctype.h>
36
37#include <mruby.h>
38#include <mruby/string.h>
39
40struct fmt_args {
41 mrb_state *mrb;
42 mrb_value str;
43};
44
45#define MAX(a,b) ((a)>(b) ? (a) : (b))
46#define MIN(a,b) ((a)<(b) ? (a) : (b))
47
48/* Convenient bit representation for modifier flags, which all fall
49 * within 31 codepoints of the space character. */
50
51#define ALT_FORM (1U<<('#'-' '))
52#define ZERO_PAD (1U<<('0'-' '))
53#define LEFT_ADJ (1U<<('-'-' '))
54#define PAD_POS (1U<<(' '-' '))
55#define MARK_POS (1U<<('+'-' '))
56
57static void
58out(struct fmt_args *f, const char *s, size_t l)
59{
60 mrb_str_cat(f->mrb, f->str, s, l);
61}
62
63#define PAD_SIZE 256
64static void
65pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl)
66{
67 char pad[PAD_SIZE];
68 if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
69 l = w - l;
70 memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
71 for (; l >= PAD_SIZE; l -= PAD_SIZE)
72 out(f, pad, PAD_SIZE);
73 out(f, pad, l);
74}
75
76static const char xdigits[16] = {
77 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
78};
79
80static char*
81fmt_u(uint32_t x, char *s)
82{
83 for (; x; x /= 10) *--s = '0' + x % 10;
84 return s;
85}
86
87/* Do not override this check. The floating point printing code below
88 * depends on the float.h constants being right. If they are wrong, it
89 * may overflow the stack. */
90#if LDBL_MANT_DIG == 53
91typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
92#endif
93
94static int
95fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
96{
97 uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
98 + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
99 uint32_t *a, *d, *r, *z;
100 uint32_t i;
101 int e2=0, e, j;
102 ptrdiff_t l;
103 char buf[9+LDBL_MANT_DIG/4], *s;
104 const char *prefix="-0X+0X 0X-0x+0x 0x";
105 ptrdiff_t pl;
106 char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
107
108 pl=1;
109 if (signbit(y)) {
110 y=-y;
111 } else if (fl & MARK_POS) {
112 prefix+=3;
113 } else if (fl & PAD_POS) {
114 prefix+=6;
115 } else prefix++, pl=0;
116
117 if (!isfinite(y)) {
118 const char *ss = (t&32)?"inf":"INF";
119 if (y!=y) ss=(t&32)?"nan":"NAN";
120 pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD);
121 out(f, prefix, pl);
122 out(f, ss, 3);
123 pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ);
124 return 3+(int)pl;
125 }
126
127 y = frexp((double)y, &e2) * 2;
128 if (y) e2--;
129
130 if ((t|32)=='a') {
131 long double round = 8.0;
132 ptrdiff_t re;
133
134 if (t&32) prefix += 9;
135 pl += 2;
136
137 if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
138 else re=LDBL_MANT_DIG/4-1-p;
139
140 if (re) {
141 while (re--) round*=16;
142 if (*prefix=='-') {
143 y=-y;
144 y-=round;
145 y+=round;
146 y=-y;
147 }
148 else {
149 y+=round;
150 y-=round;
151 }
152 }
153
154 estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
155 if (estr==ebuf) *--estr='0';
156 *--estr = (e2<0 ? '-' : '+');
157 *--estr = t+('p'-'a');
158
159 s=buf;
160 do {
161 int x=(int)y;
162 *s++=xdigits[x]|(t&32);
163 y=16*(y-x);
164 if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
165 } while (y);
166
167 if (p && s-buf-2 < p)
168 l = (p+2) + (ebuf-estr);
169 else
170 l = (s-buf) + (ebuf-estr);
171
172 pad(f, ' ', 0, pl+l, fl);
173 out(f, prefix, pl);
174 pad(f, '0', 0, pl+l, fl^ZERO_PAD);
175 out(f, buf, s-buf);
176 pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
177 out(f, estr, ebuf-estr);
178 pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
179 return (int)pl+(int)l;
180 }
181 if (p<0) p=6;
182
183 if (y) y *= 268435456.0, e2-=28;
184
185 if (e2<0) a=r=z=big;
186 else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
187
188 do {
189 *z = (uint32_t)y;
190 y = 1000000000*(y-*z++);
191 } while (y);
192
193 while (e2>0) {
194 uint32_t carry=0;
195 int sh=MIN(29,e2);
196 for (d=z-1; d>=a; d--) {
197 uint64_t x = ((uint64_t)*d<<sh)+carry;
198 *d = x % 1000000000;
199 carry = (uint32_t)(x / 1000000000);
200 }
201 if (carry) *--a = carry;
202 while (z>a && !z[-1]) z--;
203 e2-=sh;
204 }
205 while (e2<0) {
206 uint32_t carry=0, *b;
207 int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
208 for (d=a; d<z; d++) {
209 uint32_t rm = *d & ((1<<sh)-1);
210 *d = (*d>>sh) + carry;
211 carry = (1000000000>>sh) * rm;
212 }
213 if (!*a) a++;
214 if (carry) *z++ = carry;
215 /* Avoid (slow!) computation past requested precision */
216 b = (t|32)=='f' ? r : a;
217 if (z-b > need) z = b+need;
218 e2+=sh;
219 }
220
221 if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
222 else e=0;
223
224 /* Perform rounding: j is precision after the radix (possibly neg) */
225 j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
226 if (j < 9*(z-r-1)) {
227 uint32_t x;
228 /* We avoid C's broken division of negative numbers */
229 d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
230 j += 9*LDBL_MAX_EXP;
231 j %= 9;
232 for (i=10, j++; j<9; i*=10, j++);
233 x = *d % i;
234 /* Are there any significant digits past j? */
235 if (x || d+1!=z) {
236 long double round = 2/LDBL_EPSILON;
237 long double small;
238 if (*d/i & 1) round += 2;
239 if (x<i/2) small=0.5;
240 else if (x==i/2 && d+1==z) small=1.0;
241 else small=1.5;
242 if (pl && *prefix=='-') round*=-1, small*=-1;
243 *d -= x;
244 /* Decide whether to round by probing round+small */
245 if (round+small != round) {
246 *d = *d + i;
247 while (*d > 999999999) {
248 *d--=0;
249 if (d<a) *--a=0;
250 (*d)++;
251 }
252 for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
253 }
254 }
255 if (z>d+1) z=d+1;
256 }
257 for (; z>a && !z[-1]; z--);
258
259 if ((t|32)=='g') {
260 if (!p) p++;
261 if (p>e && e>=-4) {
262 t--;
263 p-=e+1;
264 }
265 else {
266 t-=2;
267 p--;
268 }
269 if (!(fl&ALT_FORM)) {
270 /* Count trailing zeros in last place */
271 if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
272 else j=9;
273 if ((t|32)=='f')
274 p = MIN(p,MAX(0,9*(z-r-1)-j));
275 else
276 p = MIN(p,MAX(0,9*(z-r-1)+e-j));
277 }
278 }
279 l = 1 + p + (p || (fl&ALT_FORM));
280 if ((t|32)=='f') {
281 if (e>0) l+=e;
282 }
283 else {
284 estr=fmt_u(e<0 ? -e : e, ebuf);
285 while(ebuf-estr<2) *--estr='0';
286 *--estr = (e<0 ? '-' : '+');
287 *--estr = t;
288 l += ebuf-estr;
289 }
290
291 pad(f, ' ', 0, pl+l, fl);
292 out(f, prefix, pl);
293 pad(f, '0', 0, pl+l, fl^ZERO_PAD);
294
295 if ((t|32)=='f') {
296 if (a>r) a=r;
297 for (d=a; d<=r; d++) {
298 char *ss = fmt_u(*d, buf+9);
299 if (d!=a) while (ss>buf) *--ss='0';
300 else if (ss==buf+9) *--ss='0';
301 out(f, ss, buf+9-ss);
302 }
303 if (p || (fl&ALT_FORM)) out(f, ".", 1);
304 for (; d<z && p>0; d++, p-=9) {
305 char *ss = fmt_u(*d, buf+9);
306 while (ss>buf) *--ss='0';
307 out(f, ss, MIN(9,p));
308 }
309 pad(f, '0', p+9, 9, 0);
310 }
311 else {
312 if (z<=a) z=a+1;
313 for (d=a; d<z && p>=0; d++) {
314 char *ss = fmt_u(*d, buf+9);
315 if (ss==buf+9) *--ss='0';
316 if (d!=a) while (ss>buf) *--ss='0';
317 else {
318 out(f, ss++, 1);
319 if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
320 }
321 out(f, ss, MIN(buf+9-ss, p));
322 p -= (int)(buf+9-ss);
323 }
324 pad(f, '0', p+18, 18, 0);
325 out(f, estr, ebuf-estr);
326 }
327
328 pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
329
330 return (int)pl+(int)l;
331}
332
333static int
334fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
335{
336 ptrdiff_t p;
337
338 if (*fmt != '%') {
339 return -1;
340 }
341 ++fmt;
342
343 if (*fmt == '.') {
344 ++fmt;
345 for (p = 0; ISDIGIT(*fmt); ++fmt) {
346 p = 10 * p + (*fmt - '0');
347 }
348 }
349 else {
350 p = -1;
351 }
352
353 switch (*fmt) {
354 case 'e': case 'f': case 'g': case 'a':
355 case 'E': case 'F': case 'G': case 'A':
356 return fmt_fp(f, flo, p, 0, *fmt);
357 default:
358 return -1;
359 }
360}
361
362mrb_value
363mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
364{
365 struct fmt_args f;
366
367 f.mrb = mrb;
368 f.str = mrb_str_new_capa(mrb, 24);
369 if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
370 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
371 }
372 return f.str;
373}
374#else /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
375#include <mruby.h>
376#include <stdio.h>
377
378mrb_value
379mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
380{
381 char buf[25];
382
383 snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
384 return mrb_str_new_cstr(mrb, buf);
385}
386#endif /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
387#endif
Note: See TracBrowser for help on using the repository browser.