source: EcnlProtoTool/trunk/mruby-1.2.0/src/fmt_fp.c@ 321

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

文字コードを設定

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