Merge pull request #6288 from dearblue/closing
[mruby.git] / src / fmt_fp.c
blobab69486e6354e8a3cd31ce564192f6bfbc60b26a
1 #include <mruby.h>
2 #include <string.h>
4 #ifndef MRB_NO_FLOAT
5 /***********************************************************************
7 Routine for converting a single-precision
8 floating-point number into a string.
10 The code in this function was inspired from Fred Bayer's pdouble.c.
11 Since pdouble.c was released as Public Domain, I'm releasing this
12 code as public domain as well.
14 Dave Hylands
16 The original code can be found in https://github.com/dhylands/format-float
17 ***********************************************************************/
19 /***********************************************************************
21 I modified the routine for mruby:
23 * support `double`
24 * support `#` (alt_form) modifier
26 My modifications in this file are also placed in the public domain.
28 Matz (Yukihiro Matsumoto)
30 ***********************************************************************/
32 #include <math.h>
34 #ifdef MRB_USE_FLOAT32
36 // 1 sign bit, 8 exponent bits, and 23 mantissa bits.
37 // exponent values 0 and 255 are reserved, exponent can be 1 to 254.
38 // exponent is stored with a bias of 127.
39 // The min and max floats are on the order of 1x10^37 and 1x10^-37
41 #define FLT_DECEXP 32
42 #define FLT_ROUND_TO_ONE 0.9999995F
43 #define FLT_MIN_BUF_SIZE 6 // -9e+99
45 #else
47 // 1 sign bit, 11 exponent bits, and 52 mantissa bits.
49 #define FLT_DECEXP 256
50 #define FLT_ROUND_TO_ONE 0.999999999995
51 #define FLT_MIN_BUF_SIZE 7 // -9e+199
53 #endif /* MRB_USE_FLOAT32 */
55 static const mrb_float g_pos_pow[] = {
56 #ifndef MRB_USE_FLOAT32
57 1e256, 1e128, 1e64,
58 #endif
59 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
61 static const mrb_float g_neg_pow[] = {
62 #ifndef MRB_USE_FLOAT32
63 1e-256, 1e-128, 1e-64,
64 #endif
65 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
69 * mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign)
71 * fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#')
72 * prec: is the precision (as specified in printf)
73 * sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print
74 * a sign if ```f``` is negative. Anything else is printed as the
75 * sign character for positive numbers.
78 int
79 mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
80 char *s = buf;
81 int buf_remaining = (int)buf_size - 1;
82 int alt_form = 0;
84 if ((uint8_t)fmt & 0x80) {
85 fmt &= 0x7f; /* turn off alt_form flag */
86 alt_form = 1;
88 if (buf_size <= FLT_MIN_BUF_SIZE) {
89 // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating
90 // null.
92 if (buf_size >= 2) {
93 *s++ = '?';
95 if (buf_size >= 1) {
96 *s++ = '\0';
98 return buf_size >= 2;
100 if (signbit(f)) {
101 *s++ = '-';
102 f = -f;
104 else if (sign) {
105 *s++ = sign;
107 buf_remaining -= (int)(s - buf); // Adjust for sign
110 char uc = fmt & 0x20;
111 if (isinf(f)) {
112 *s++ = 'I' ^ uc;
113 *s++ = 'N' ^ uc;
114 *s++ = 'F' ^ uc;
115 goto ret;
117 else if (isnan(f)) {
118 *s++ = 'N' ^ uc;
119 *s++ = 'A' ^ uc;
120 *s++ = 'N' ^ uc;
121 ret:
122 *s = '\0';
123 return (int)(s - buf);
127 if (prec < 0) {
128 prec = 6;
130 char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
131 fmt |= 0x20; // Force fmt to be lowercase
132 char org_fmt = fmt;
133 if (fmt == 'g' && prec == 0) {
134 prec = 1;
136 int e, e1;
137 int dec = 0;
138 char e_sign = '\0';
139 int num_digits = 0;
140 const mrb_float *pos_pow = g_pos_pow;
141 const mrb_float *neg_pow = g_neg_pow;
143 if (f == 0.0) {
144 e = 0;
145 if (fmt == 'e') {
146 e_sign = '+';
148 else if (fmt == 'f') {
149 num_digits = prec + 1;
152 else if (f < 1.0) { // f < 1.0
153 char first_dig = '0';
154 if (f >= FLT_ROUND_TO_ONE) {
155 first_dig = '1';
158 // Build negative exponent
159 for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
160 if (*neg_pow > f) {
161 e += e1;
162 f *= *pos_pow;
165 char e_sign_char = '-';
166 if (f < 1.0) {
167 if (f >= FLT_ROUND_TO_ONE) {
168 f = 1.0;
169 if (e == 0) {
170 e_sign_char = '+';
173 else {
174 e++;
175 f *= 10.0;
179 // If the user specified 'g' format, and e is <= 4, then we'll switch
180 // to the fixed format ('f')
182 if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
183 fmt = 'f';
184 dec = -1;
185 *s++ = first_dig;
187 if (org_fmt == 'g') {
188 prec += (e - 1);
190 // truncate precision to prevent buffer overflow
191 if (prec + 2 > buf_remaining) {
192 prec = buf_remaining - 2;
194 num_digits = prec;
195 if (num_digits || alt_form) {
196 *s++ = '.';
197 while (--e && num_digits) {
198 *s++ = '0';
199 num_digits--;
203 else {
204 // For e & g formats, we'll be printing the exponent, so set the
205 // sign.
206 e_sign = e_sign_char;
207 dec = 0;
209 if (prec > (buf_remaining - FLT_MIN_BUF_SIZE)) {
210 prec = buf_remaining - FLT_MIN_BUF_SIZE;
211 if (fmt == 'g') {
212 prec++;
217 else {
218 // Build positive exponent
219 for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
220 if (*pos_pow <= f) {
221 e += e1;
222 f *= *neg_pow;
226 // If the user specified fixed format (fmt == 'f') and e makes the
227 // number too big to fit into the available buffer, then we'll
228 // switch to the 'e' format.
230 if (fmt == 'f') {
231 if (e >= buf_remaining) {
232 fmt = 'e';
234 else if ((e + prec + 2) > buf_remaining) {
235 prec = buf_remaining - e - 2;
236 if (prec < 0) {
237 // This means no decimal point, so we can add one back
238 // for the decimal.
239 prec++;
243 if (fmt == 'e' && prec > (buf_remaining - 6)) {
244 prec = buf_remaining - 6;
246 // If the user specified 'g' format, and e is < prec, then we'll switch
247 // to the fixed format.
249 if (fmt == 'g' && e < prec) {
250 fmt = 'f';
251 prec -= (e + 1);
253 if (fmt == 'f') {
254 dec = e;
255 num_digits = prec + e + 1;
257 else {
258 e_sign = '+';
261 if (prec < 0) {
262 // This can happen when the prec is trimmed to prevent buffer overflow
263 prec = 0;
266 // We now have f as a floating-point number between >= 1 and < 10
267 // (or equal to zero), and e contains the absolute value of the power of
268 // 10 exponent, and (dec + 1) == the number of digits before the decimal.
270 // For e, prec is # digits after the decimal
271 // For f, prec is # digits after the decimal
272 // For g, prec is the max number of significant digits
274 // For e & g there will be a single digit before the decimal
275 // for f there will be e digits before the decimal
277 if (fmt == 'e') {
278 num_digits = prec + 1;
279 if (prec == 0) prec = 1;
281 else if (fmt == 'g') {
282 num_digits = prec;
285 // Print the digits of the mantissa
286 for (int i = 0; i < num_digits; i++,dec--) {
287 int8_t d = (int8_t)((int)f)%10;
288 *s++ = '0' + d;
289 if (dec == 0 && (prec > 0 || alt_form)) {
290 *s++ = '.';
292 f -= (mrb_float)d;
293 f *= 10.0;
296 // Round
297 if (f >= 5.0) {
298 char *rs = s;
299 rs--;
300 while (1) {
301 if (*rs == '.') {
302 rs--;
303 continue;
305 if (*rs < '0' || *rs > '9') {
306 // + or -
307 rs++; // So we sit on the digit to the right of the sign
308 break;
310 if (*rs < '9') {
311 (*rs)++;
312 break;
314 *rs = '0';
315 if (rs == buf) {
316 break;
318 rs--;
320 if (*rs == '0') {
321 // We need to insert a 1
322 if (fmt != 'f' && rs[1] == '.') {
323 // We're going to round 9.99 to 10.00
324 // Move the decimal point
325 rs[0] = '.';
326 rs[1] = '0';
327 if (e_sign == '-') {
328 e--;
330 else {
331 e++;
334 s++;
335 char *ss = s;
336 while (ss > rs) {
337 *ss = ss[-1];
338 ss--;
340 *rs = '1';
341 if (f < 1.0 && fmt == 'f') {
342 // We rounded up to 1.0
343 prec--;
348 if (org_fmt == 'g' && prec > 0 && !alt_form) {
349 // Remove trailing zeros and a trailing decimal point
350 while (s[-1] == '0') {
351 s--;
353 if (s[-1] == '.') {
354 s--;
357 // Append the exponent
358 if (e_sign) {
359 *s++ = e_char;
360 *s++ = e_sign;
361 if (e >= 100) {
362 *s++ = '0' + (e / 100);
363 e %= 100;
365 *s++ = '0' + (e / 10);
366 *s++ = '0' + (e % 10);
369 *s = '\0';
370 return (int)(s - buf);
372 #endif