fmt_fp.c: replace with public domain float format routine; ref #5448

The original code can be found in `https://github.com/dhylands/format-float`.

Changes:
- support `double`
- support `#` (alt_form) modifier
- small refactoring
parent fa29bb3b
......@@ -165,13 +165,14 @@ MRB_API mrb_float mrb_as_float(mrb_state *mrb, mrb_value x);
/* internal functions */
mrb_float mrb_div_float(mrb_float x, mrb_float y);
/* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */
mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x);
int mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float f);
int mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign);
/* obsolete functions; will be removed */
#define mrb_flo_to_fixnum(mrb, val) mrb_float_to_integer(mrb, val)
#define mrb_to_flo(mrb, x) mrb_as_float(mrb, x)
/* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */
int mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float f);
#endif /* MRB_NO_FLOAT */
......
#include <mruby.h>
#include <string.h>
#include <stdlib.h>
#ifndef MRB_NO_FLOAT
#ifdef MRB_USE_FLOAT32
#define FLO_TO_STR_PREC 7
......@@ -7,451 +9,447 @@
#define FLO_TO_STR_PREC 15
#endif
#if defined(MRB_NO_STDIO) || defined(_WIN32) || defined(_WIN64)
/*
/***********************************************************************
Most code in this file originates from musl (src/stdio/vfprintf.c)
which, just like mruby itself, is licensed under the MIT license.
Routine for converting a single-precision
floating point number into a string.
Copyright (c) 2005-2014 Rich Felker, et al.
The code in this funcion was inspired from Fred Bayer's pdouble.c.
Since pdouble.c was released as Public Domain, I'm releasing this
code as public domain as well.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Dave Hylands
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
The original code can be found in https://github.com/dhylands/format-float
***********************************************************************/
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/***********************************************************************
*/
I modified the routine for mruby:
#include <limits.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <ctype.h>
* support `double`
* support `#` (alt_form) modifier
#include <mruby/string.h>
My modifications in this file are also placed in the public domain.
struct fmt_args;
Matz (Yukihiro Matsumoto)
typedef void output_func(struct fmt_args *f, const char *s, size_t l);
***********************************************************************/
struct fmt_args {
mrb_state *mrb;
output_func *output;
void *opaque;
};
#include <math.h>
struct mrb_cstr {
char *buf;
size_t len;
};
#ifdef MRB_USE_FLOAT32
#define MAX(a,b) ((a)>(b) ? (a) : (b))
#define MIN(a,b) ((a)<(b) ? (a) : (b))
// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
// exponent is stored with a bias of 127.
// The min and max floats are on the order of 1x10^37 and 1x10^-37
/* Convenient bit representation for modifier flags, which all fall
* within 31 codepoints of the space character. */
#define FLT_DECEXP 32
#define FLT_ROUND_TO_ONE 0.9999995F
#define FLT_MIN_BUF_SIZE 6 // -9e+99
#define ALT_FORM (1U<<('#'-' '))
#define ZERO_PAD (1U<<('0'-' '))
#define LEFT_ADJ (1U<<('-'-' '))
#define PAD_POS (1U<<(' '-' '))
#define MARK_POS (1U<<('+'-' '))
#else
#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS)
// 1 sign bit, 11 exponent bits, and 52 mantissa bits.
static output_func strcat_value;
static output_func strcat_cstr;
#define FLT_DECEXP 256
#define FLT_ROUND_TO_ONE 0.999999999995
#define FLT_MIN_BUF_SIZE 7 // -9e+199
static void
strcat_value(struct fmt_args *f, const char *s, size_t l)
{
mrb_value str = *(mrb_value*)f->opaque;
mrb_str_cat(f->mrb, str, s, l);
}
#endif /* MRB_USE_FLOAT32 */
static void
strcat_cstr(struct fmt_args *f, const char *s, size_t l)
{
struct mrb_cstr *cstr = (struct mrb_cstr*)f->opaque;
static const mrb_float g_pos_pow[] = {
#ifndef MRB_USE_FLOAT32
1e256, 1e128, 1e64,
#endif
1e32, 1e16, 1e8, 1e4, 1e2, 1e1
};
static const mrb_float g_neg_pow[] = {
#ifndef MRB_USE_FLOAT32
1e-256, 1e-128, 1e-64,
#endif
1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
};
if (l > cstr->len) {
mrb_state *mrb = f->mrb;
/*
* mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign)
*
* fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#')
* prec: is the precision (as specified in printf)
* sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print
* a sign if ```f``` is negative. Anything else is printed as the
* sign character for positive nubers.
*/
mrb_raise(mrb, E_ARGUMENT_ERROR, "string buffer too small");
int
mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
char *s = buf;
int buf_remaining = buf_size - 1;
int alt_form = 0;
if ((uint8_t)fmt & 0x80) {
fmt &= 0x7f; /* turn off alt_form flag */
alt_form = 1;
}
if (buf_size <= FLT_MIN_BUF_SIZE) {
// Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating
// null.
memcpy(cstr->buf, s, l);
if (buf_size >= 2) {
*s++ = '?';
}
if (buf_size >= 1) {
*s++ = '\0';
}
return buf_size >= 2;
}
if (signbit(f)) {
*s++ = '-';
f = -f;
} else if (sign) {
*s++ = sign;
}
buf_remaining -= (s - buf); // Adjust for sign
cstr->buf += l;
cstr->len -= l;
}
{
char uc = fmt & 0x20;
if (isinf(f)) {
*s++ = 'I' ^ uc;
*s++ = 'N' ^ uc;
*s++ = 'F' ^ uc;
goto ret;
} else if (isnan(f)) {
*s++ = 'N' ^ uc;
*s++ = 'A' ^ uc;
*s++ = 'N' ^ uc;
ret:
*s = '\0';
return s - buf;
}
}
static void
out(struct fmt_args *f, const char *s, size_t l)
{
f->output(f, s, l);
}
if (prec < 0) {
prec = 6;
}
char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
fmt |= 0x20; // Force fmt to be lowercase
char org_fmt = fmt;
if (fmt == 'g' && prec == 0) {
prec = 1;
}
int e, e1;
int dec = 0;
char e_sign = '\0';
int num_digits = 0;
const mrb_float *pos_pow = g_pos_pow;
const mrb_float *neg_pow = g_neg_pow;
#define PAD_SIZE 256
static void
pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint32_t fl)
{
char pad[PAD_SIZE];
if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
l = w - l;
memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
for (; l >= PAD_SIZE; l -= PAD_SIZE)
out(f, pad, PAD_SIZE);
out(f, pad, l);
}
if (f == 0.0) {
e = 0;
if (fmt == 'e') {
e_sign = '+';
} else if (fmt == 'f') {
num_digits = prec + 1;
}
} else if (f < 1.0) { // f < 1.0
char first_dig = '0';
if (f >= FLT_ROUND_TO_ONE) {
first_dig = '1';
}
static const char xdigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
// Build negative exponent
for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
if (*neg_pow > f) {
e += e1;
f *= *pos_pow;
}
}
char e_sign_char = '-';
if (f < 1.0) {
if (f >= FLT_ROUND_TO_ONE) {
f = 1.0;
if (e == 0) {
e_sign_char = '+';
}
} else {
e++;
f *= 10.0;
}
}
static char*
fmt_u(uint32_t x, char *s)
{
for (; x; x /= 10) *--s = '0' + x % 10;
return s;
}
// If the user specified 'g' format, and e is <= 4, then we'll switch
// to the fixed format ('f')
/* Do not override this check. The floating-point printing code below
* depends on the float.h constants being right. If they are wrong, it
* may overflow the stack. */
#if LDBL_MANT_DIG == 53
typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
#endif
if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
fmt = 'f';
dec = -1;
*s++ = first_dig;
static int
fmt_fp(struct fmt_args *f, long double y, ptrdiff_t w, ptrdiff_t p, uint32_t fl, int t)
{
uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
+ (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
uint32_t *a, *d, *r, *z;
uint32_t i;
int e2=0, e, j;
ptrdiff_t l;
char buf[9+LDBL_MANT_DIG/4], *s;
const char *prefix="-0X+0X 0X-0x+0x 0x";
ptrdiff_t pl;
char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
pl=1;
if (signbit(y)) {
y=-y;
} else if (fl & MARK_POS) {
prefix+=3;
} else if (fl & PAD_POS) {
prefix+=6;
} else prefix++, pl=0;
if (!isfinite(y)) {
const char *ss = (t&32)?"inf":"INF";
if (y!=y) ss=(t&32)?"nan":"NAN";
pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
out(f, prefix, pl);
out(f, ss, 3);
pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
return (int)MAX(w, 3+pl);
}
y = frexp((double)y, &e2) * 2;
if (y) e2--;
if ((t|32)=='a') {
long double round = 8.0;
ptrdiff_t re;
if (t&32) prefix += 9;
pl += 2;
if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
else re=LDBL_MANT_DIG/4-1-p;
if (re) {
while (re--) round*=16;
if (*prefix=='-') {
y=-y;
y-=round;
y+=round;
y=-y;
if (prec + e + 1 > buf_remaining) {
prec = buf_remaining - e - 1;
}
else {
y+=round;
y-=round;
}
}
estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
if (estr==ebuf) *--estr='0';
*--estr = (e2<0 ? '-' : '+');
*--estr = t+('p'-'a');
s=buf;
do {
int x=(int)y;
*s++=xdigits[x]|(t&32);
y=16*(y-x);
if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
} while (y);
if (p && s-buf-2 < p)
l = (p+2) + (ebuf-estr);
else
l = (s-buf) + (ebuf-estr);
pad(f, ' ', w, pl+l, fl);
out(f, prefix, pl);
pad(f, '0', w, pl+l, fl^ZERO_PAD);
out(f, buf, s-buf);
pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
out(f, estr, ebuf-estr);
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
return (int)MAX(w, pl+l);
}
if (p<0) p=6;
if (y) y *= 268435456.0, e2-=28;
if (e2<0) a=r=z=big;
else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
do {
*z = (uint32_t)y;
y = 1000000000*(y-*z++);
} while (y);
while (e2>0) {
uint32_t carry=0;
int sh=MIN(29,e2);
for (d=z-1; d>=a; d--) {
uint64_t x = ((uint64_t)*d<<sh)+carry;
*d = x % 1000000000;
carry = (uint32_t)(x / 1000000000);
}
if (carry) *--a = carry;
while (z>a && !z[-1]) z--;
e2-=sh;
}
while (e2<0) {
uint32_t carry=0, *b;
int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
for (d=a; d<z; d++) {
uint32_t rm = *d & ((1<<sh)-1);
*d = (*d>>sh) + carry;
carry = (1000000000>>sh) * rm;
}
if (!*a) a++;
if (carry) *z++ = carry;
/* Avoid (slow!) computation past requested precision */
b = (t|32)=='f' ? r : a;
if (z-b > need) z = b+need;
e2+=sh;
}
if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
else e=0;
/* Perform rounding: j is precision after the radix (possibly neg) */
j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
if (j < 9*(z-r-1)) {
uint32_t x;
/* We avoid C's broken division of negative numbers */
d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
j += 9*LDBL_MAX_EXP;
j %= 9;
for (i=10, j++; j<9; i*=10, j++);
x = *d % i;
/* Are there any significant digits past j? */
if (x || d+1!=z) {
long double round = 2/LDBL_EPSILON;
long double small;
if (*d/i & 1) round += 2;
if (x<i/2) small=0.5;
else if (x==i/2 && d+1==z) small=1.0;
else small=1.5;
if (pl && *prefix=='-') round*=-1, small*=-1;
*d -= x;
/* Decide whether to round by probing round+small */
if (round+small != round) {
*d = *d + i;
while (*d > 999999999) {
*d--=0;
if (d<a) *--a=0;
(*d)++;
}
for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
}
}
if (z>d+1) z=d+1;
}
for (; z>a && !z[-1]; z--);
if ((t|32)=='g') {
if (!p) p++;
if (p>e && e>=-4) {
t--;
p-=e+1;
if (org_fmt == 'g') {
prec += (e - 1);
}
else {
t-=2;
p--;
num_digits = prec;
if (num_digits || alt_form) {
*s++ = '.';
while (--e && num_digits) {
*s++ = '0';
num_digits--;
}
if (!(fl&ALT_FORM)) {
/* Count trailing zeros in last place */
if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
else j=9;
if ((t|32)=='f')
p = MIN(p,MAX(0,9*(z-r-1)-j));
else
p = MIN(p,MAX(0,9*(z-r-1)+e-j));
}
} else {
// For e & g formats, we'll be printing the exponent, so set the
// sign.
e_sign = e_sign_char;
dec = 0;
if (prec > (buf_remaining - 6)) {
prec = buf_remaining - 6;
if (fmt == 'g') {
prec++;
}
l = 1 + p + (p || (fl&ALT_FORM));
if ((t|32)=='f') {
if (e>0) l+=e;
}
else {
estr=fmt_u(e<0 ? -e : e, ebuf);
while(ebuf-estr<2) *--estr='0';
*--estr = (e<0 ? '-' : '+');
*--estr = t;
l += ebuf-estr;
}
} else {
// Build positive exponent
for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
if (*pos_pow <= f) {
e += e1;
f *= *neg_pow;
}
}
pad(f, ' ', w, pl+l, fl);
out(f, prefix, pl);
pad(f, '0', w, pl+l, fl^ZERO_PAD);
// If the user specified fixed format (fmt == 'f') and e makes the
// number too big to fit into the available buffer, then we'll
// switch to the 'e' format.
if ((t|32)=='f') {
if (a>r) a=r;
for (d=a; d<=r; d++) {
char *ss = fmt_u(*d, buf+9);
if (d!=a) while (ss>buf) *--ss='0';
else if (ss==buf+9) *--ss='0';
out(f, ss, buf+9-ss);
if (fmt == 'f') {
if (e >= buf_remaining) {
fmt = 'e';
} else if ((e + prec + 2) > buf_remaining) {
prec = buf_remaining - e - 2;
if (prec < 0) {
// This means no decimal point, so we can add one back
// for the decimal.
prec++;
}
if (p || (fl&ALT_FORM)) out(f, ".", 1);
for (; d<z && p>0; d++, p-=9) {
char *ss = fmt_u(*d, buf+9);
while (ss>buf) *--ss='0';
out(f, ss, MIN(9,p));
}
pad(f, '0', p+9, 9, 0);
}
else {
if (z<=a) z=a+1;
for (d=a; d<z && p>=0; d++) {
char *ss = fmt_u(*d, buf+9);
if (ss==buf+9) *--ss='0';
if (d!=a) while (ss>buf) *--ss='0';
else {
out(f, ss++, 1);
if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
if (fmt == 'e' && prec > (buf_remaining - 6)) {
prec = buf_remaining - 6;
}
out(f, ss, MIN(buf+9-ss, p));
p -= (int)(buf+9-ss);
// If the user specified 'g' format, and e is < prec, then we'll switch
// to the fixed format.
if (fmt == 'g' && e < prec) {
fmt = 'f';
prec -= (e + 1);
}
pad(f, '0', p+18, 18, 0);
out(f, estr, ebuf-estr);
if (fmt == 'f') {
dec = e;
num_digits = prec + e + 1;
} else {
e_sign = '+';
}
}
if (prec < 0) {
// This can happen when the prec is trimmed to prevent buffer overflow
prec = 0;
}
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
return (int)MAX(w, pl+l);
}
// We now have f as a floating point number between >= 1 and < 10
// (or equal to zero), and e contains the absolute value of the power of
// 10 exponent. and (dec + 1) == the number of dgits before the decimal.
static int
fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
{
ptrdiff_t w, p;
uint32_t fl;
// For e, prec is # digits after the decimal
// For f, prec is # digits after the decimal
// For g, prec is the max number of significant digits
//
// For e & g there will be a single digit before the decimal
// for f there will be e digits before the decimal
if (*fmt != '%') {
return -1;
if (fmt == 'e') {
num_digits = prec + 1;
} else if (fmt == 'g') {
if (prec == 0) {
prec = 1;
}
num_digits = prec;
}
++fmt;
/* Read modifier flags */
for (fl=0; (unsigned)*fmt-' '<32 && (FLAGMASK&(1U<<(*fmt-' '))); fmt++)
fl |= 1U<<(*fmt-' ');
/* - and 0 flags are mutually exclusive */
if (fl & LEFT_ADJ) fl &= ~ZERO_PAD;
for (w = 0; ISDIGIT(*fmt); ++fmt) {
w = 10 * w + (*fmt - '0');
// Print the digits of the mantissa
for (int i = 0; i < num_digits; ++i, --dec) {
int8_t d = f;
*s++ = '0' + d;
if (dec == 0 && (prec > 0 || alt_form)) {
*s++ = '.';
}
f -= (mrb_float)d;
f *= 10.0;
}
if (*fmt == '.') {
++fmt;
for (p = 0; ISDIGIT(*fmt); ++fmt) {
p = 10 * p + (*fmt - '0');
// Round
if (f >= 5.0) {
char *rs = s;
rs--;
while (1) {
if (*rs == '.') {
rs--;
continue;
}
if (*rs < '0' || *rs > '9') {
// + or -
rs++; // So we sit on the digit to the right of the sign
break;
}
if (*rs < '9') {
(*rs)++;
break;
}
*rs = '0';
if (rs == buf) {
break;
}
rs--;
}
if (*rs == '0') {
// We need to insert a 1
if (rs[1] == '.' && fmt != 'f') {
// We're going to round 9.99 to 10.00
// Move the decimal point
rs[0] = '.';
rs[1] = '0';
if (e_sign == '-') {
e--;
} else {
e++;
}
}
s++;
char *ss = s;
while (ss > rs) {
*ss = ss[-1];
ss--;
}
*rs = '1';
if (f < 1.0 && fmt == 'f') {
// We rounded up to 1.0
prec--;
}
}
else {
p = -1;
}
switch (*fmt) {
case 'e': case 'f': case 'g': case 'a':
case 'E': case 'F': case 'G': case 'A':
return fmt_fp(f, flo, w, p, fl, *fmt);
default:
return -1;
if (org_fmt == 'g' && prec > 0 && !alt_form) {
// Remove trailing zeros and a trailing decimal point
while (s[-1] == '0') {
s--;
}
}
if (s[-1] == '.') {
s--;
}
}
// Append the exponent
if (e_sign) {
*s++ = e_char;
*s++ = e_sign;
*s++ = '0' + (e / 10);
*s++ = '0' + (e % 10);
}
*s = '\0';
int
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float fval)
{
struct fmt_args f;
struct mrb_cstr cstr;
cstr.buf = buf;
cstr.len = len - 1; /* reserve NUL terminator */
f.mrb = mrb;
f.output = strcat_cstr;
f.opaque = (void*)&cstr;
if (fmt_core(&f, fmt, fval) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
}
*cstr.buf = '\0';
return (int)(cstr.buf - buf);
return s - buf;
}
#else /* MRB_NO_STDIO || _WIN32 || _WIN64 */
#include <stdio.h>
int
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float fval)
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t buf_size, const char *fmt, mrb_float f)
{
return snprintf(buf, len, fmt, fval);
}
const char *s = fmt;
char sign = '\0';
int alt_form = 0;
int left_align = 0;
int zero_pad = 0;
int width = -1;
int prec = 6;
s++; // skip %
while (*s) {
if (*s == '-') {
left_align = 1;
} else if (*s == '+') {
sign = '+';
} else if (*s == ' ') {
sign = ' ';
} else if (*s == '0') {
zero_pad = 1;
} else if (*s == '#') {
alt_form = 1;
} else {
break;
}
s++;
}
if (ISDIGIT(*s)) {
char *endptr;
width = strtoul(s, &endptr, 10);
s = endptr;
}
if (*s == '.') {
s++;
if (ISDIGIT(*s)) {
char *endptr;
prec = strtoul(s, &endptr, 10);
s = endptr;
}
else {
prec = 0;
}
}
char c = *s;
if (alt_form) {
c |= 0x80;
}
int len = mrb_format_float(f, buf, buf_size, c, prec, sign);
#endif /* MRB_NO_STDIO || _WIN32 || _WIN64 */
// buf[0] < '0' returns true if the first character is space, + or -
// buf[1] < '9' matches a digit, and doesn't match when we get back +nan or +inf
if (buf[0] < '0' && buf[1] <= '9' && zero_pad) {
buf++;
width--;
len--;
}
if (*buf < '0' || *buf >= '9') {
// For inf or nan, we don't want to zero pad.
zero_pad = 0;
}
if (len >= width) {
return len;
}
buf[width] = '\0';
if (left_align) {
memset(&buf[len], ' ', width - len);
return width;
}
memmove(&buf[width - len], buf, len);
if (zero_pad) {
memset(buf, '0', width - len);
} else {
memset(buf, ' ', width - len);
}
return width;
}
MRB_API mrb_value
mrb_float_to_str(mrb_state *mrb, mrb_value flo)
{
char fmt[] = "%." MRB_STRINGIZE(FLO_TO_STR_PREC) "g";
char buf[25];
mrb_float_to_cstr(mrb, buf, sizeof(buf), fmt, mrb_float(flo));
mrb_format_float(mrb_float(flo), buf, sizeof(buf), 'g', FLO_TO_STR_PREC, '\0');
for (char *p = buf; *p; p++) {
if (*p == '.') goto exit;
if (*p == 'e') {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment