#ifndef MRB_WITHOUT_FLOAT
#if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
#include <limits.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <float.h>
#include <ctype.h>
#include <mruby.h>
#include <mruby/string.h>
struct fmt_args {
mrb_state *mrb;
mrb_value str;
};
#define MAX(a,b) ((a)>(b) ? (a) : (b))
#define MIN(a,b) ((a)<(b) ? (a) : (b))
#define ALT_FORM (1U<<('#'-' '))
#define ZERO_PAD (1U<<('0'-' '))
#define LEFT_ADJ (1U<<('-'-' '))
#define PAD_POS (1U<<(' '-' '))
#define MARK_POS (1U<<('+'-' '))
static void
out(struct fmt_args *f, const char *s, size_t l)
{
mrb_str_cat(f->mrb, f->str, s, l);
}
#define PAD_SIZE 256
static void
pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_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);
}
static const char xdigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
static char*
fmt_u(uint32_t x, char *s)
{
for (; x; x /= 10) *--s = '0' + x % 10;
return s;
}
#if LDBL_MANT_DIG == 53
typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
#endif
static int
fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
{
uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; 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, ' ', 0, 3+pl, fl&~ZERO_PAD);
out(f, prefix, pl);
out(f, ss, 3);
pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ);
return 3+(int)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;
}
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, ' ', 0, pl+l, fl);
out(f, prefix, pl);
pad(f, '0', 0, 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, ' ', 0, pl+l, fl^LEFT_ADJ);
return (int)pl+(int)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;
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;
j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
if (j < 9*(z-r-1)) {
uint32_t x;
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;
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;
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;
}
else {
t-=2;
p--;
}
if (!(fl&ALT_FORM)) {
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));
}
}
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;
}
pad(f, ' ', 0, pl+l, fl);
out(f, prefix, pl);
pad(f, '0', 0, pl+l, fl^ZERO_PAD);
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 (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);
}
out(f, ss, MIN(buf+9-ss, p));
p -= (int)(buf+9-ss);
}
pad(f, '0', p+18, 18, 0);
out(f, estr, ebuf-estr);
}
pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
return (int)pl+(int)l;
}
static int
fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
{
ptrdiff_t p;
if (*fmt != '%') {
return -1;
}
++fmt;
if (*fmt == '.') {
++fmt;
for (p = 0; ISDIGIT(*fmt); ++fmt) {
p = 10 * p + (*fmt - '0');
}
}
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, p, 0, *fmt);
default:
return -1;
}
}
mrb_value
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
{
struct fmt_args f;
f.mrb = mrb;
f.str = mrb_str_new_capa(mrb, 24);
if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
}
return f.str;
}
#else
#include <mruby.h>
#include <stdio.h>
mrb_value
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
{
char buf[25];
snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
return mrb_str_new_cstr(mrb, buf);
}
#endif
#endif