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.
16 The original code can be found in https://github.com/dhylands/format-float
17 ***********************************************************************/
19 /***********************************************************************
21 I modified the routine for mruby:
24 * support `#` (alt_form) modifier
26 My modifications in this file are also placed in the public domain.
28 Matz (Yukihiro Matsumoto)
30 ***********************************************************************/
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
42 #define FLT_ROUND_TO_ONE 0.9999995F
43 #define FLT_MIN_BUF_SIZE 6 // -9e+99
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
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,
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.
79 mrb_format_float(mrb_float f
, char *buf
, size_t buf_size
, char fmt
, int prec
, char sign
) {
81 int buf_remaining
= (int)buf_size
- 1;
84 if ((uint8_t)fmt
& 0x80) {
85 fmt
&= 0x7f; /* turn off alt_form flag */
88 if (buf_size
<= FLT_MIN_BUF_SIZE
) {
89 // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating
107 buf_remaining
-= (int)(s
- buf
); // Adjust for sign
110 char uc
= fmt
& 0x20;
123 return (int)(s
- buf
);
130 char e_char
= 'E' | (fmt
& 0x20); // e_char will match case of fmt
131 fmt
|= 0x20; // Force fmt to be lowercase
133 if (fmt
== 'g' && prec
== 0) {
140 const mrb_float
*pos_pow
= g_pos_pow
;
141 const mrb_float
*neg_pow
= g_neg_pow
;
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
) {
158 // Build negative exponent
159 for (e
= 0, e1
= FLT_DECEXP
; e1
; e1
>>= 1, pos_pow
++, neg_pow
++) {
165 char e_sign_char
= '-';
167 if (f
>= FLT_ROUND_TO_ONE
) {
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)) {
187 if (org_fmt
== 'g') {
190 // truncate precision to prevent buffer overflow
191 if (prec
+ 2 > buf_remaining
) {
192 prec
= buf_remaining
- 2;
195 if (num_digits
|| alt_form
) {
197 while (--e
&& num_digits
) {
204 // For e & g formats, we'll be printing the exponent, so set the
206 e_sign
= e_sign_char
;
209 if (prec
> (buf_remaining
- FLT_MIN_BUF_SIZE
)) {
210 prec
= buf_remaining
- FLT_MIN_BUF_SIZE
;
218 // Build positive exponent
219 for (e
= 0, e1
= FLT_DECEXP
; e1
; e1
>>= 1, pos_pow
++, 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.
231 if (e
>= buf_remaining
) {
234 else if ((e
+ prec
+ 2) > buf_remaining
) {
235 prec
= buf_remaining
- e
- 2;
237 // This means no decimal point, so we can add one back
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
) {
255 num_digits
= prec
+ e
+ 1;
262 // This can happen when the prec is trimmed to prevent buffer overflow
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
278 num_digits
= prec
+ 1;
279 if (prec
== 0) prec
= 1;
281 else if (fmt
== 'g') {
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;
289 if (dec
== 0 && (prec
> 0 || alt_form
)) {
305 if (*rs
< '0' || *rs
> '9') {
307 rs
++; // So we sit on the digit to the right of the sign
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
341 if (f
< 1.0 && fmt
== 'f') {
342 // We rounded up to 1.0
348 if (org_fmt
== 'g' && prec
> 0 && !alt_form
) {
349 // Remove trailing zeros and a trailing decimal point
350 while (s
[-1] == '0') {
357 // Append the exponent
362 *s
++ = '0' + (e
/ 100);
365 *s
++ = '0' + (e
/ 10);
366 *s
++ = '0' + (e
% 10);
370 return (int)(s
- buf
);