1 // stb_sprintf - v1.02 - public domain snprintf() implementation
2 // originally by Jeff Roberts / RAD Game Tools, 2015/10/20
3 // http://github.com/nothings/stb
5 // allowed types: sc uidBboXx p AaGgEef n
6 // lengths : h ll j z t I64 I32 I
8 // Contributors (bugfixes):
12 #ifndef STB_SPRINTF_H_INCLUDE
13 #define STB_SPRINTF_H_INCLUDE
16 Single file sprintf replacement.
18 Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
19 Hereby placed in public domain.
21 This is a full sprintf replacement that supports everything that
22 the C runtime sprintfs support, including float/double, 64-bit integers,
23 hex floats, field parameters (%*.*d stuff), length reads backs, etc.
25 Why would you need this if sprintf already exists? Well, first off,
26 it's *much* faster (see below). It's also much smaller than the CRT
27 versions code-space-wise. We've also added some simple improvements
28 that are super handy (commas in thousands, callbacks at buffer full,
29 for example). Finally, the format strings for MSVC and GCC differ
30 for 64-bit integers (among other small things), so this lets you use
31 the same format strings in cross platform code.
33 It uses the standard single file trick of being both the header file
34 and the source itself. If you just include it normally, you just get
35 the header file function definitions. To get the code, you include
36 it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
38 It only uses va_args macros from the C runtime to do it's work. It
39 does cast doubles to S64s and shifts and divides U64s, which does
40 drag in CRT code on most platforms.
42 It compiles to roughly 8K with float support, and 4K without.
43 As a comparison, when using MSVC static libs, calling sprintf drags
48 int stbsp_sprintf( char * buf, char const * fmt, ... )
49 int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
50 Convert an arg list into a buffer. stbsp_snprintf always returns
51 a zero-terminated string (unlike regular snprintf).
53 int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
54 int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
55 Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
56 a zero-terminated string (unlike regular snprintf).
58 int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
59 typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
60 Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
61 Your callback can then copy the chars out, print them or whatever.
62 This function is actually the workhorse for everything else.
63 The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
64 // you return the next buffer to use or 0 to stop converting
66 void stbsp_set_separators( char comma, char period )
67 Set the comma and period characters to use.
71 This code uses a internal float->ascii conversion method that uses
72 doubles with error correction (double-doubles, for ~105 bits of
73 precision). This conversion is round-trip perfect - that is, an atof
74 of the values output here will give you the bit-exact double back.
76 One difference is that our insignificant digits will be different than
77 with MSVC or GCC (but they don't match each other either). We also
78 don't attempt to find the minimum length matching float (pre-MSVC15
81 If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
82 and you'll save 4K of code space.
86 This library also supports 64-bit integers and you can use MSVC style or
87 GCC style indicators (%I64d or %lld). It supports the C99 specifiers
88 for size_t and ptr_diff_t (%jd %zd) as well.
92 Like some GCCs, for integers and floats, you can use a ' (single quote)
93 specifier and commas will be inserted on the thousands: "%'d" on 12345
96 For integers and floats, you can use a "$" specifier and the number
97 will be converted to float and then divided to get kilo, mega, giga or
98 tera and then printed, so "%$d" 1024 is "1.0 k", "%$.2d" 2536000 is
101 In addition to octal and hexadecimal conversions, you can print
102 integers in binary: "%b" for 256 would print 100.
104 PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
105 ===================================================================
106 "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
107 "%24d" across all 32-bit ints (4.5x/4.2x faster)
108 "%x" across all 32-bit ints (4.5x/3.8x faster)
109 "%08x" across all 32-bit ints (4.3x/3.8x faster)
110 "%f" across e-10 to e+10 floats (7.3x/6.0x faster)
111 "%e" across e-10 to e+10 floats (8.1x/6.0x faster)
112 "%g" across e-10 to e+10 floats (10.0x/7.1x faster)
113 "%f" for values near e-300 (7.9x/6.5x faster)
114 "%f" for values near e+300 (10.0x/9.1x faster)
115 "%e" for values near e-300 (10.1x/7.0x faster)
116 "%e" for values near e+300 (9.2x/6.0x faster)
117 "%.320f" for values near e-300 (12.6x/11.2x faster)
118 "%a" for random values (8.6x/4.3x faster)
119 "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
120 "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
121 "%s%s%s" for 64 char strings (7.1x/7.3x faster)
122 "...512 char string..." ( 35.0x/32.5x faster!)
125 #if defined(__has_feature)
126 #if __has_feature(address_sanitizer)
127 #define STBI__ASAN __attribute__((no_sanitize("address")))
134 #ifdef STB_SPRINTF_STATIC
135 #define STBSP__PUBLICDEC static
136 #define STBSP__PUBLICDEF static STBI__ASAN
139 #define STBSP__PUBLICDEC extern "C"
140 #define STBSP__PUBLICDEF extern "C" STBI__ASAN
142 #define STBSP__PUBLICDEC extern
143 #define STBSP__PUBLICDEF STBI__ASAN
147 #include <stdarg.h> // for va_list()
149 #ifndef STB_SPRINTF_MIN
150 #define STB_SPRINTF_MIN 512 // how many characters per callback
152 typedef char * STBSP_SPRINTFCB( char * buf
, void * user
, int len
);
154 #ifndef STB_SPRINTF_DECORATE
155 #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
158 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( vsprintf
)( char * buf
, char const * fmt
, va_list va
);
159 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( vsnprintf
)( char * buf
, int count
, char const * fmt
, va_list va
);
160 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( sprintf
) ( char * buf
, char const * fmt
, ... );
161 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( snprintf
)( char * buf
, int count
, char const * fmt
, ... );
163 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( vsprintfcb
)( STBSP_SPRINTFCB
* callback
, void * user
, char * buf
, char const * fmt
, va_list va
);
164 STBSP__PUBLICDEF
void STB_SPRINTF_DECORATE( set_separators
)( char comma
, char period
);
166 #endif // STB_SPRINTF_H_INCLUDE
168 #ifdef STB_SPRINTF_IMPLEMENTATION
170 #include <stdlib.h> // for va_arg()
172 #define stbsp__uint32 unsigned int
173 #define stbsp__int32 signed int
176 #define stbsp__uint64 unsigned __int64
177 #define stbsp__int64 signed __int64
179 #define stbsp__uint64 unsigned long long
180 #define stbsp__int64 signed long long
182 #define stbsp__uint16 unsigned short
184 #ifndef stbsp__uintptr
185 #if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
186 #define stbsp__uintptr stbsp__uint64
188 #define stbsp__uintptr stbsp__uint32
192 #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
193 #if defined(_MSC_VER) && (_MSC_VER<1900)
194 #define STB_SPRINTF_MSVC_MODE
198 #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
199 #define STBSP__UNALIGNED(code)
201 #define STBSP__UNALIGNED(code) code
204 #ifndef STB_SPRINTF_NOFLOAT
205 // internal float utility functions
206 static stbsp__int32
stbsp__real_to_str( char const * * start
, stbsp__uint32
* len
, char *out
, stbsp__int32
* decimal_pos
, double value
, stbsp__uint32 frac_digits
);
207 static stbsp__int32
stbsp__real_to_parts( stbsp__int64
* bits
, stbsp__int32
* expo
, double value
);
208 #define STBSP__SPECIAL 0x7000
211 static char stbsp__period
='.';
212 static char stbsp__comma
=',';
213 static char stbsp__digitpair
[201]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
215 STBSP__PUBLICDEF
void STB_SPRINTF_DECORATE( set_separators
)( char pcomma
, char pperiod
)
217 stbsp__period
=pperiod
;
221 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( vsprintfcb
)( STBSP_SPRINTFCB
* callback
, void * user
, char * buf
, char const * fmt
, va_list va
)
223 static char hex
[]="0123456789abcdefxp";
224 static char hexu
[]="0123456789ABCDEFXP";
233 stbsp__int32 fw
,pr
,tz
; stbsp__uint32 fl
;
235 #define STBSP__LEFTJUST 1
236 #define STBSP__LEADINGPLUS 2
237 #define STBSP__LEADINGSPACE 4
238 #define STBSP__LEADING_0X 8
239 #define STBSP__LEADINGZERO 16
240 #define STBSP__INTMAX 32
241 #define STBSP__TRIPLET_COMMA 64
242 #define STBSP__NEGATIVE 128
243 #define STBSP__METRIC_SUFFIX 256
244 #define STBSP__HALFWIDTH 512
246 // macros for the callback buffer stuff
247 #define stbsp__chk_cb_bufL(bytes) { int len = (int)(bf-buf); if ((len+(bytes))>=STB_SPRINTF_MIN) { tlen+=len; if (0==(bf=buf=callback(buf,user,len))) goto done; } }
248 #define stbsp__chk_cb_buf(bytes) { if ( callback ) { stbsp__chk_cb_bufL(bytes); } }
249 #define stbsp__flush_cb() { stbsp__chk_cb_bufL(STB_SPRINTF_MIN-1); } //flush if there is even one byte in the buffer
250 #define stbsp__cb_buf_clamp(cl,v) cl = v; if ( callback ) { int lg = STB_SPRINTF_MIN-(int)(bf-buf); if (cl>lg) cl=lg; }
252 // fast copy everything up to the next % (or end of string)
255 while (((stbsp__uintptr
)f
)&3)
257 schk1
: if (f
[0]=='%') goto scandd
;
258 schk2
: if (f
[0]==0) goto endfmt
;
259 stbsp__chk_cb_buf(1); *bf
++=f
[0]; ++f
;
263 // Check if the next 4 bytes contain %(0x25) or end of string.
264 // Using the 'hasless' trick:
265 // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
267 v
=*(stbsp__uint32
*)f
; c
=(~v
)&0x80808080;
268 if (((v
^0x25252525)-0x01010101)&c
) goto schk1
;
269 if ((v
-0x01010101)&c
) goto schk2
;
270 if (callback
) if ((STB_SPRINTF_MIN
-(int)(bf
-buf
))<4) goto schk1
;
271 *(stbsp__uint32
*)bf
=v
; bf
+=4; f
+=4;
277 // ok, we have a percent, read the modifiers first
278 fw
= 0; pr
= -1; fl
= 0; tz
= 0;
285 // if we have left justify
286 case '-': fl
|=STBSP__LEFTJUST
; ++f
; continue;
287 // if we have leading plus
288 case '+': fl
|=STBSP__LEADINGPLUS
; ++f
; continue;
289 // if we have leading space
290 case ' ': fl
|=STBSP__LEADINGSPACE
; ++f
; continue;
291 // if we have leading 0x
292 case '#': fl
|=STBSP__LEADING_0X
; ++f
; continue;
293 // if we have thousand commas
294 case '\'': fl
|=STBSP__TRIPLET_COMMA
; ++f
; continue;
295 // if we have kilo marker
296 case '$': fl
|=STBSP__METRIC_SUFFIX
; ++f
; continue;
297 // if we have leading zero
298 case '0': fl
|=STBSP__LEADINGZERO
; ++f
; goto flags_done
;
299 default: goto flags_done
;
304 // get the field width
305 if ( f
[0] == '*' ) {fw
= va_arg(va
,stbsp__uint32
); ++f
;} else { while (( f
[0] >= '0' ) && ( f
[0] <= '9' )) { fw
= fw
* 10 + f
[0] - '0'; f
++; } }
307 if ( f
[0]=='.' ) { ++f
; if ( f
[0] == '*' ) {pr
= va_arg(va
,stbsp__uint32
); ++f
;} else { pr
= 0; while (( f
[0] >= '0' ) && ( f
[0] <= '9' )) { pr
= pr
* 10 + f
[0] - '0'; f
++; } } }
309 // handle integer size overrides
313 case 'h': fl
|=STBSP__HALFWIDTH
; ++f
; break;
314 // are we 64-bit (unix style)
315 case 'l': ++f
; if ( f
[0]=='l') { fl
|=STBSP__INTMAX
; ++f
; } break;
316 // are we 64-bit on intmax? (c99)
317 case 'j': fl
|=STBSP__INTMAX
; ++f
; break;
318 // are we 64-bit on size_t or ptrdiff_t? (c99)
319 case 'z': case 't': fl
|=((sizeof(char*)==8)?STBSP__INTMAX
:0); ++f
; break;
320 // are we 64-bit (msft style)
321 case 'I': if ( ( f
[1]=='6') && ( f
[2]=='4') ) { fl
|=STBSP__INTMAX
; f
+=3; }
322 else if ( ( f
[1]=='3') && ( f
[2]=='2') ) { f
+=3; }
323 else { fl
|=((sizeof(void*)==8)?STBSP__INTMAX
:0); ++f
; } break;
327 // handle each replacement
330 #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
331 char num
[STBSP__NUMSZ
];
336 stbsp__uint32 l
,n
,cs
;
338 #ifndef STB_SPRINTF_NOFLOAT
341 stbsp__int32 dp
; char const * sn
;
345 s
= va_arg(va
,char*); if (s
==0) s
= (char*)"null";
350 if ((((stbsp__uintptr
)sn
)&3)==0) break;
352 if (sn
[0]==0) goto ld
;
356 if (pr
>=0) { n
=(stbsp__uint32
)(sn
-s
); if (n
>=(stbsp__uint32
)pr
) goto ld
; n
=((stbsp__uint32
)(pr
-n
))>>2; }
359 stbsp__uint32 v
=*(stbsp__uint32
*)sn
;
360 if ((v
-0x01010101)&(~v
)&0x80808080UL
) goto lchk
;
367 l
= (stbsp__uint32
) ( sn
- s
);
368 // clamp to precision
369 if ( l
> (stbsp__uint32
)pr
) l
= pr
;
370 lead
[0]=0; tail
[0]=0; pr
= 0; dp
= 0; cs
= 0;
371 // copy the string in
376 s
= num
+ STBSP__NUMSZ
-1; *s
= (char)va_arg(va
,int);
378 lead
[0]=0; tail
[0]=0; pr
= 0; dp
= 0; cs
= 0;
381 case 'n': // weird write-bytes specifier
382 { int * d
= va_arg(va
,int*);
383 *d
= tlen
+ (int)( bf
- buf
); }
386 #ifdef STB_SPRINTF_NOFLOAT
388 case 'a': // hex float
394 va_arg(va
,double); // eat it
395 s
= (char*)"No float";
397 lead
[0]=0; tail
[0]=0; pr
= 0; dp
= 0; cs
= 0;
404 case 'a': // hex float
407 fv
= va_arg(va
,double);
408 if (pr
==-1) pr
=6; // default is 6
409 // read the double into a string
410 if ( stbsp__real_to_parts( (stbsp__int64
*)&n64
, &dp
, fv
) )
411 fl
|= STBSP__NEGATIVE
;
416 lead
[0]=0; if (fl
&STBSP__NEGATIVE
) { lead
[0]=1; lead
[1]='-'; } else if (fl
&STBSP__LEADINGSPACE
) { lead
[0]=1; lead
[1]=' '; } else if (fl
&STBSP__LEADINGPLUS
) { lead
[0]=1; lead
[1]='+'; };
418 if (dp
==-1023) dp
=(n64
)?-1022:0; else n64
|=(((stbsp__uint64
)1)<<52);
420 if (pr
<15) n64
+=((((stbsp__uint64
)8)<<56)>>(pr
*4));
423 #ifdef STB_SPRINTF_MSVC_MODE
426 lead
[1+lead
[0]]='0'; lead
[2+lead
[0]]='x'; lead
[0]+=2;
428 *s
++=h
[(n64
>>60)&15]; n64
<<=4;
429 if ( pr
) *s
++=stbsp__period
;
433 n
= pr
; if (n
>13) n
= 13; if (pr
>(stbsp__int32
)n
) tz
=pr
-n
; pr
= 0;
434 while(n
--) { *s
++=h
[(n64
>>60)&15]; n64
<<=4; }
438 if (dp
<0) { tail
[2]='-'; dp
=-dp
;} else tail
[2]='+';
439 n
= (dp
>=1000)?6:((dp
>=100)?5:((dp
>=10)?4:3));
441 for(;;) { tail
[n
]='0'+dp
%10; if (n
<=3) break; --n
; dp
/=10; }
444 l
= (int)(s
-(num
+64));
456 fv
= va_arg(va
,double);
457 if (pr
==-1) pr
=6; else if (pr
==0) pr
= 1; // default is 6
458 // read the double into a string
459 if ( stbsp__real_to_str( &sn
, &l
, num
, &dp
, fv
, (pr
-1)|0x80000000 ) )
460 fl
|= STBSP__NEGATIVE
;
462 // clamp the precision and delete extra zeros after clamp
464 if ( l
> (stbsp__uint32
)pr
) l
= pr
; while ((l
>1)&&(pr
)&&(sn
[l
-1]=='0')) { --pr
; --l
; }
467 if ((dp
<=-4)||(dp
>(stbsp__int32
)n
))
469 if ( pr
> (stbsp__int32
)l
) pr
= l
-1; else if ( pr
) --pr
; // when using %e, there is one digit before the decimal
472 // this is the insane action to get the pr to match %g sematics for %f
473 if(dp
>0) { pr
=(dp
<(stbsp__int32
)l
)?l
-dp
:0; } else { pr
= -dp
+((pr
>(stbsp__int32
)l
)?l
:pr
); }
483 fv
= va_arg(va
,double);
484 if (pr
==-1) pr
=6; // default is 6
485 // read the double into a string
486 if ( stbsp__real_to_str( &sn
, &l
, num
, &dp
, fv
, pr
|0x80000000 ) )
487 fl
|= STBSP__NEGATIVE
;
490 lead
[0]=0; if (fl
&STBSP__NEGATIVE
) { lead
[0]=1; lead
[1]='-'; } else if (fl
&STBSP__LEADINGSPACE
) { lead
[0]=1; lead
[1]=' '; } else if (fl
&STBSP__LEADINGPLUS
) { lead
[0]=1; lead
[1]='+'; };
491 if ( dp
== STBSP__SPECIAL
) { s
=(char*)sn
; cs
=0; pr
=0; goto scopy
; }
493 // handle leading chars
496 if (pr
) *s
++=stbsp__period
;
498 // handle after decimal
499 if ((l
-1)>(stbsp__uint32
)pr
) l
=pr
+1;
500 for(n
=1;n
<l
;n
++) *s
++=sn
[n
];
506 if (dp
<0) { tail
[2]='-'; dp
=-dp
;} else tail
[2]='+';
507 #ifdef STB_SPRINTF_MSVC_MODE
513 for(;;) { tail
[n
]='0'+dp
%10; if (n
<=3) break; --n
; dp
/=10; }
514 cs
= 1 + (3<<24); // how many tens
518 fv
= va_arg(va
,double);
521 if (fl
&STBSP__METRIC_SUFFIX
) {while(fl
<0x4000000) { if ((fv
<1024.0) && (fv
>-1024.0)) break; fv
/=1024.0; fl
+=0x1000000; }}
522 if (pr
==-1) pr
=6; // default is 6
523 // read the double into a string
524 if ( stbsp__real_to_str( &sn
, &l
, num
, &dp
, fv
, pr
) )
525 fl
|= STBSP__NEGATIVE
;
529 lead
[0]=0; if (fl
&STBSP__NEGATIVE
) { lead
[0]=1; lead
[1]='-'; } else if (fl
&STBSP__LEADINGSPACE
) { lead
[0]=1; lead
[1]=' '; } else if (fl
&STBSP__LEADINGPLUS
) { lead
[0]=1; lead
[1]='+'; };
530 if ( dp
== STBSP__SPECIAL
) { s
=(char*)sn
; cs
=0; pr
=0; goto scopy
; }
533 // handle the three decimal varieties
537 // handle 0.000*000xxxx
538 *s
++='0'; if (pr
) *s
++=stbsp__period
;
539 n
=-dp
; if((stbsp__int32
)n
>pr
) n
=pr
; i
=n
; while(i
) { if ((((stbsp__uintptr
)s
)&3)==0) break; *s
++='0'; --i
; } while(i
>=4) { *(stbsp__uint32
*)s
=0x30303030; s
+=4; i
-=4; } while(i
) { *s
++='0'; --i
; }
540 if ((stbsp__int32
)(l
+n
)>pr
) l
=pr
-n
; i
=l
; while(i
) { *s
++=*sn
++; --i
; }
542 cs
= 1 + (3<<24); // how many tens did we write (for commas below)
546 cs
= (fl
&STBSP__TRIPLET_COMMA
)?((600-(stbsp__uint32
)dp
)%3):0;
547 if ((stbsp__uint32
)dp
>=l
)
549 // handle xxxx000*000.0
550 n
=0; for(;;) { if ((fl
&STBSP__TRIPLET_COMMA
) && (++cs
==4)) { cs
= 0; *s
++=stbsp__comma
; } else { *s
++=sn
[n
]; ++n
; if (n
>=l
) break; } }
551 if (n
<(stbsp__uint32
)dp
)
554 if ((fl
&STBSP__TRIPLET_COMMA
)==0) { while(n
) { if ((((stbsp__uintptr
)s
)&3)==0) break; *s
++='0'; --n
; } while(n
>=4) { *(stbsp__uint32
*)s
=0x30303030; s
+=4; n
-=4; } }
555 while(n
) { if ((fl
&STBSP__TRIPLET_COMMA
) && (++cs
==4)) { cs
= 0; *s
++=stbsp__comma
; } else { *s
++='0'; --n
; } }
557 cs
= (int)(s
-(num
+64)) + (3<<24); // cs is how many tens
558 if (pr
) { *s
++=stbsp__period
; tz
=pr
;}
562 // handle xxxxx.xxxx000*000
563 n
=0; for(;;) { if ((fl
&STBSP__TRIPLET_COMMA
) && (++cs
==4)) { cs
= 0; *s
++=stbsp__comma
; } else { *s
++=sn
[n
]; ++n
; if (n
>=(stbsp__uint32
)dp
) break; } }
564 cs
= (int)(s
-(num
+64)) + (3<<24); // cs is how many tens
565 if (pr
) *s
++=stbsp__period
;
566 if ((l
-dp
)>(stbsp__uint32
)pr
) l
=pr
+dp
;
567 while(n
<l
) { *s
++=sn
[n
]; ++n
; }
574 if (fl
&STBSP__METRIC_SUFFIX
) { tail
[0]=1; tail
[1]=' '; { if (fl
>>24) { tail
[2]="_kmgt"[fl
>>24]; tail
[0]=2; } } };
577 // get the length that we copied
578 l
= (stbsp__uint32
) ( s
-(num
+64) );
583 case 'B': // upper binary
587 case 'b': // lower binary
591 if (fl
&STBSP__LEADING_0X
) { lead
[0]=2;lead
[1]='0';lead
[2]=h
[0xb]; }
598 if (fl
&STBSP__LEADING_0X
) { lead
[0]=1;lead
[1]='0'; }
603 fl
|= (sizeof(void*)==8)?STBSP__INTMAX
:0;
604 pr
= sizeof(void*)*2;
605 fl
&= ~STBSP__LEADINGZERO
; // 'p' only prints the pointer with zeros
608 case 'X': // upper binary
612 case 'x': // lower binary
616 if (fl
&STBSP__LEADING_0X
) { lead
[0]=2;lead
[1]='0';lead
[2]=h
[16]; }
619 if ( fl
&STBSP__INTMAX
)
620 n64
= va_arg(va
,stbsp__uint64
);
622 n64
= va_arg(va
,stbsp__uint32
);
624 s
= num
+ STBSP__NUMSZ
; dp
= 0;
625 // clear tail, and clear leading if value is zero
626 tail
[0]=0; if (n64
==0) { lead
[0]=0; if (pr
==0) { l
=0; cs
= ( ((l
>>4)&15)) << 24; goto scopy
; } }
628 for(;;) { *--s
= h
[n64
&((1<<(l
>>8))-1)]; n64
>>=(l
>>8); if ( ! ( (n64
) || ((stbsp__int32
) ( (num
+STBSP__NUMSZ
) - s
) < pr
) ) ) break; if ( fl
&STBSP__TRIPLET_COMMA
) { ++l
; if ((l
&15)==((l
>>4)&15)) { l
&=~15; *--s
=stbsp__comma
; } } };
629 // get the tens and the comma pos
630 cs
= (stbsp__uint32
) ( (num
+STBSP__NUMSZ
) - s
) + ( ( ((l
>>4)&15)) << 24 );
631 // get the length that we copied
632 l
= (stbsp__uint32
) ( (num
+STBSP__NUMSZ
) - s
);
636 case 'u': // unsigned
639 // get the integer and abs it
640 if ( fl
&STBSP__INTMAX
)
642 stbsp__int64 i64
= va_arg(va
,stbsp__int64
); n64
= (stbsp__uint64
)i64
; if ((f
[0]!='u') && (i64
<0)) { n64
=(stbsp__uint64
)-i64
; fl
|=STBSP__NEGATIVE
; }
646 stbsp__int32 i
= va_arg(va
,stbsp__int32
); n64
= (stbsp__uint32
)i
; if ((f
[0]!='u') && (i
<0)) { n64
=(stbsp__uint32
)-i
; fl
|=STBSP__NEGATIVE
; }
649 #ifndef STB_SPRINTF_NOFLOAT
650 if (fl
&STBSP__METRIC_SUFFIX
) { if (n64
<1024) pr
=0; else if (pr
==-1) pr
=1; fv
=(double)(stbsp__int64
)n64
; goto doafloat
; }
654 s
= num
+STBSP__NUMSZ
; l
=0;
658 // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
660 if (n64
>=100000000) { n
= (stbsp__uint32
)( n64
% 100000000); n64
/= 100000000; } else {n
= (stbsp__uint32
)n64
; n64
= 0; }
661 if((fl
&STBSP__TRIPLET_COMMA
)==0) { while(n
) { s
-=2; *(stbsp__uint16
*)s
=*(stbsp__uint16
*)&stbsp__digitpair
[(n
%100)*2]; n
/=100; } }
662 while (n
) { if ( ( fl
&STBSP__TRIPLET_COMMA
) && (l
++==3) ) { l
=0; *--s
=stbsp__comma
; --o
; } else { *--s
=(char)(n
%10)+'0'; n
/=10; } }
663 if (n64
==0) { if ((s
[0]=='0') && (s
!=(num
+STBSP__NUMSZ
))) ++s
; break; }
664 while (s
!=o
) if ( ( fl
&STBSP__TRIPLET_COMMA
) && (l
++==3) ) { l
=0; *--s
=stbsp__comma
; --o
; } else { *--s
='0'; }
669 lead
[0]=0; if (fl
&STBSP__NEGATIVE
) { lead
[0]=1; lead
[1]='-'; } else if (fl
&STBSP__LEADINGSPACE
) { lead
[0]=1; lead
[1]=' '; } else if (fl
&STBSP__LEADINGPLUS
) { lead
[0]=1; lead
[1]='+'; };
671 // get the length that we copied
672 l
= (stbsp__uint32
) ( (num
+STBSP__NUMSZ
) - s
); if ( l
== 0 ) { *--s
='0'; l
= 1; }
677 // get fw=leading/trailing space, pr=leading zeros
678 if (pr
<(stbsp__int32
)l
) pr
= l
;
679 n
= pr
+ lead
[0] + tail
[0] + tz
;
680 if (fw
<(stbsp__int32
)n
) fw
= n
;
684 // handle right justify and leading zeros
685 if ( (fl
&STBSP__LEFTJUST
)==0 )
687 if (fl
&STBSP__LEADINGZERO
) // if leading zeros, everything is in pr
694 fl
&= ~STBSP__TRIPLET_COMMA
; // if no leading zeros, then no commas
698 // copy the spaces and/or zeros
701 stbsp__int32 i
; stbsp__uint32 c
;
703 // copy leading spaces (or when doing %8.4d stuff)
704 if ( (fl
&STBSP__LEFTJUST
)==0 ) while(fw
>0) { stbsp__cb_buf_clamp(i
,fw
); fw
-= i
; while(i
) { if ((((stbsp__uintptr
)bf
)&3)==0) break; *bf
++=' '; --i
; } while(i
>=4) { *(stbsp__uint32
*)bf
=0x20202020; bf
+=4; i
-=4; } while (i
) {*bf
++=' '; --i
;} stbsp__chk_cb_buf(1); }
707 sn
=lead
+1; while(lead
[0]) { stbsp__cb_buf_clamp(i
,lead
[0]); lead
[0] -= (char)i
; while (i
) {*bf
++=*sn
++; --i
;} stbsp__chk_cb_buf(1); }
709 // copy leading zeros
710 c
= cs
>> 24; cs
&= 0xffffff;
711 cs
= (fl
&STBSP__TRIPLET_COMMA
)?((stbsp__uint32
)(c
-((pr
+cs
)%(c
+1)))):0;
712 while(pr
>0) { stbsp__cb_buf_clamp(i
,pr
); pr
-= i
; if((fl
&STBSP__TRIPLET_COMMA
)==0) { while(i
) { if ((((stbsp__uintptr
)bf
)&3)==0) break; *bf
++='0'; --i
; } while(i
>=4) { *(stbsp__uint32
*)bf
=0x30303030; bf
+=4; i
-=4; } } while (i
) { if((fl
&STBSP__TRIPLET_COMMA
) && (cs
++==c
)) { cs
= 0; *bf
++=stbsp__comma
; } else *bf
++='0'; --i
; } stbsp__chk_cb_buf(1); }
715 // copy leader if there is still one
716 sn
=lead
+1; while(lead
[0]) { stbsp__int32 i
; stbsp__cb_buf_clamp(i
,lead
[0]); lead
[0] -= (char)i
; while (i
) {*bf
++=*sn
++; --i
;} stbsp__chk_cb_buf(1); }
719 n
= l
; while (n
) { stbsp__int32 i
; stbsp__cb_buf_clamp(i
,n
); n
-=i
; STBSP__UNALIGNED( while(i
>=4) { *(stbsp__uint32
*)bf
=*(stbsp__uint32
*)s
; bf
+=4; s
+=4; i
-=4; } ) while (i
) {*bf
++=*s
++; --i
;} stbsp__chk_cb_buf(1); }
721 // copy trailing zeros
722 while(tz
) { stbsp__int32 i
; stbsp__cb_buf_clamp(i
,tz
); tz
-= i
; while(i
) { if ((((stbsp__uintptr
)bf
)&3)==0) break; *bf
++='0'; --i
; } while(i
>=4) { *(stbsp__uint32
*)bf
=0x30303030; bf
+=4; i
-=4; } while (i
) {*bf
++='0'; --i
;} stbsp__chk_cb_buf(1); }
724 // copy tail if there is one
725 sn
=tail
+1; while(tail
[0]) { stbsp__int32 i
; stbsp__cb_buf_clamp(i
,tail
[0]); tail
[0] -= (char)i
; while (i
) {*bf
++=*sn
++; --i
;} stbsp__chk_cb_buf(1); }
727 // handle the left justify
728 if (fl
&STBSP__LEFTJUST
) if (fw
>0) { while (fw
) { stbsp__int32 i
; stbsp__cb_buf_clamp(i
,fw
); fw
-=i
; while(i
) { if ((((stbsp__uintptr
)bf
)&3)==0) break; *bf
++=' '; --i
; } while(i
>=4) { *(stbsp__uint32
*)bf
=0x20202020; bf
+=4; i
-=4; } while (i
--) *bf
++=' '; stbsp__chk_cb_buf(1); } }
731 default: // unknown, just copy code
732 s
= num
+ STBSP__NUMSZ
-1; *s
= f
[0];
735 lead
[0]=0; tail
[0]=0; pr
= 0; dp
= 0; cs
= 0;
748 return tlen
+ (int)(bf
-buf
);
752 #undef STBSP__LEFTJUST
753 #undef STBSP__LEADINGPLUS
754 #undef STBSP__LEADINGSPACE
755 #undef STBSP__LEADING_0X
756 #undef STBSP__LEADINGZERO
758 #undef STBSP__TRIPLET_COMMA
759 #undef STBSP__NEGATIVE
760 #undef STBSP__METRIC_SUFFIX
762 #undef stbsp__chk_cb_bufL
763 #undef stbsp__chk_cb_buf
764 #undef stbsp__flush_cb
765 #undef stbsp__cb_buf_clamp
767 // ============================================================================
770 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( sprintf
)( char * buf
, char const * fmt
, ... )
775 result
= STB_SPRINTF_DECORATE( vsprintfcb
)( 0, 0, buf
, fmt
, va
);
780 typedef struct stbsp__context
784 char tmp
[ STB_SPRINTF_MIN
];
787 static char * stbsp__clamp_callback( char * buf
, void * user
, int len
)
789 stbsp__context
* c
= (stbsp__context
*)user
;
791 if ( len
> c
->count
) len
= c
->count
;
798 d
= c
->buf
; s
= buf
; se
= buf
+len
;
799 do{ *d
++ = *s
++; } while (s
<se
);
805 if ( c
->count
<= 0 ) return 0;
806 return ( c
->count
>= STB_SPRINTF_MIN
) ? c
->buf
: c
->tmp
; // go direct into buffer if you can
809 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( vsnprintf
)( char * buf
, int count
, char const * fmt
, va_list va
)
820 STB_SPRINTF_DECORATE( vsprintfcb
)( stbsp__clamp_callback
, &c
, stbsp__clamp_callback(0,&c
,0), fmt
, va
);
823 l
= (int)( c
.buf
- buf
);
824 if ( l
>= count
) // should never be greater, only equal (or less) than count
831 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( snprintf
)( char * buf
, int count
, char const * fmt
, ... )
837 result
= STB_SPRINTF_DECORATE( vsnprintf
)( buf
, count
, fmt
, va
);
843 STBSP__PUBLICDEF
int STB_SPRINTF_DECORATE( vsprintf
)( char * buf
, char const * fmt
, va_list va
)
845 return STB_SPRINTF_DECORATE( vsprintfcb
)( 0, 0, buf
, fmt
, va
);
848 // =======================================================================
849 // low level float utility functions
851 #ifndef STB_SPRINTF_NOFLOAT
853 // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
854 #define STBSP__COPYFP(dest,src) { int cn; for(cn=0;cn<8;cn++) ((char*)&dest)[cn]=((char*)&src)[cn]; }
857 static stbsp__int32
stbsp__real_to_parts( stbsp__int64
* bits
, stbsp__int32
* expo
, double value
)
862 // load value and round at the frac_digits
865 STBSP__COPYFP( b
, d
);
867 *bits
= b
& ((((stbsp__uint64
)1)<<52)-1);
868 *expo
= (stbsp__int32
) (((b
>> 52) & 2047)-1023);
870 return (stbsp__int32
)(b
>> 63);
873 static double const stbsp__bot
[23]={1e+000,1e+001,1e+002,1e+003,1e+004,1e+005,1e+006,1e+007,1e+008,1e+009,1e+010,1e+011,1e+012,1e+013,1e+014,1e+015,1e+016,1e+017,1e+018,1e+019,1e+020,1e+021,1e+022};
874 static double const stbsp__negbot
[22]={1e-001,1e-002,1e-003,1e-004,1e-005,1e-006,1e-007,1e-008,1e-009,1e-010,1e-011,1e-012,1e-013,1e-014,1e-015,1e-016,1e-017,1e-018,1e-019,1e-020,1e-021,1e-022};
875 static double const stbsp__negboterr
[22]={-5.551115123125783e-018,-2.0816681711721684e-019,-2.0816681711721686e-020,-4.7921736023859299e-021,-8.1803053914031305e-022,4.5251888174113741e-023,4.5251888174113739e-024,-2.0922560830128471e-025,-6.2281591457779853e-026,-3.6432197315497743e-027,6.0503030718060191e-028,2.0113352370744385e-029,-3.0373745563400371e-030,1.1806906454401013e-032,-7.7705399876661076e-032,2.0902213275965398e-033,-7.1542424054621921e-034,-7.1542424054621926e-035,2.4754073164739869e-036,5.4846728545790429e-037,9.2462547772103625e-038,-4.8596774326570872e-039};
876 static double const stbsp__top
[13]={1e+023,1e+046,1e+069,1e+092,1e+115,1e+138,1e+161,1e+184,1e+207,1e+230,1e+253,1e+276,1e+299};
877 static double const stbsp__negtop
[13]={1e-023,1e-046,1e-069,1e-092,1e-115,1e-138,1e-161,1e-184,1e-207,1e-230,1e-253,1e-276,1e-299};
878 static double const stbsp__toperr
[13]={8388608,6.8601809640529717e+028,-7.253143638152921e+052,-4.3377296974619174e+075,-1.5559416129466825e+098,-3.2841562489204913e+121,-3.7745893248228135e+144,-1.7356668416969134e+167,-3.8893577551088374e+190,-9.9566444326005119e+213,6.3641293062232429e+236,-5.2069140800249813e+259,-5.2504760255204387e+282};
879 static double const stbsp__negtoperr
[13]={3.9565301985100693e-040,-2.299904345391321e-063,3.6506201437945798e-086,1.1875228833981544e-109,-5.0644902316928607e-132,-6.7156837247865426e-155,-2.812077463003139e-178,-5.7778912386589953e-201,7.4997100559334532e-224,-4.6439668915134491e-247,-6.3691100762962136e-270,-9.436808465446358e-293,8.0970921678014997e-317};
881 #if defined(_MSC_VER) && (_MSC_VER<=1200)
882 static stbsp__uint64
const stbsp__powten
[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000,100000000000, 1000000000000,10000000000000,100000000000000,1000000000000000, 10000000000000000,100000000000000000,1000000000000000000,10000000000000000000U };
883 #define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
885 static stbsp__uint64
const stbsp__powten
[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000ULL,100000000000ULL, 1000000000000ULL,10000000000000ULL,100000000000000ULL,1000000000000000ULL, 10000000000000000ULL,100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL };
886 #define stbsp__tento19th (1000000000000000000ULL)
889 #define stbsp__ddmulthi(oh,ol,xh,yh) \
891 double ahi=0,alo,bhi=0,blo; \
894 STBSP__COPYFP(bt,xh); bt&=((~(stbsp__uint64)0)<<27); STBSP__COPYFP(ahi,bt); alo = xh-ahi; \
895 STBSP__COPYFP(bt,yh); bt&=((~(stbsp__uint64)0)<<27); STBSP__COPYFP(bhi,bt); blo = yh-bhi; \
896 ol = ((ahi*bhi-oh)+ahi*blo+alo*bhi)+alo*blo; \
899 #define stbsp__ddtoS64(ob,xh,xl) \
901 double ahi=0,alo,vh,t;\
902 ob = (stbsp__int64)ph;\
906 alo = (xh-(ahi-t))-(vh+t);\
907 ob += (stbsp__int64)(ahi+alo+xl);\
911 #define stbsp__ddrenorm(oh,ol) { double s; s=oh+ol; ol=ol-(s-oh); oh=s; }
913 #define stbsp__ddmultlo(oh,ol,xh,xl,yh,yl) \
914 ol = ol + ( xh*yl + xl*yh ); \
916 #define stbsp__ddmultlos(oh,ol,xh,yl) \
917 ol = ol + ( xh*yl ); \
919 static void stbsp__raise_to_power10( double *ohi, double *olo, double d, stbsp__int32 power ) // power can be -323 to +350
922 if ((power
>=0) && (power
<=22))
924 stbsp__ddmulthi(ph
,pl
,d
,stbsp__bot
[power
]);
928 stbsp__int32 e
,et
,eb
;
931 e
=power
; if (power
<0) e
=-e
;
932 et
= (e
*0x2c9)>>14;/* %23 */ if (et
>13) et
=13; eb
= e
-(et
*23);
937 if (eb
) { --eb
; stbsp__ddmulthi(ph
,pl
,d
,stbsp__negbot
[eb
]); stbsp__ddmultlos(ph
,pl
,d
,stbsp__negboterr
[eb
]); }
940 stbsp__ddrenorm(ph
,pl
);
941 --et
; stbsp__ddmulthi(p2h
,p2l
,ph
,stbsp__negtop
[et
]); stbsp__ddmultlo(p2h
,p2l
,ph
,pl
,stbsp__negtop
[et
],stbsp__negtoperr
[et
]); ph
=p2h
;pl
=p2l
;
948 e
= eb
; if (eb
>22) eb
=22; e
-= eb
;
949 stbsp__ddmulthi(ph
,pl
,d
,stbsp__bot
[eb
]);
950 if ( e
) { stbsp__ddrenorm(ph
,pl
); stbsp__ddmulthi(p2h
,p2l
,ph
,stbsp__bot
[e
]); stbsp__ddmultlos(p2h
,p2l
,stbsp__bot
[e
],pl
); ph
=p2h
;pl
=p2l
; }
954 stbsp__ddrenorm(ph
,pl
);
955 --et
; stbsp__ddmulthi(p2h
,p2l
,ph
,stbsp__top
[et
]); stbsp__ddmultlo(p2h
,p2l
,ph
,pl
,stbsp__top
[et
],stbsp__toperr
[et
]); ph
=p2h
;pl
=p2l
;
959 stbsp__ddrenorm(ph
,pl
);
960 *ohi
= ph
; *olo
= pl
;
963 // given a float value, returns the significant bits in bits, and the position of the
964 // decimal point in decimal_pos. +/-INF and NAN are specified by special values
965 // returned in the decimal_pos parameter.
966 // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
967 static stbsp__int32
stbsp__real_to_str( char const * * start
, stbsp__uint32
* len
, char *out
, stbsp__int32
* decimal_pos
, double value
, stbsp__uint32 frac_digits
)
970 stbsp__int64 bits
= 0;
971 stbsp__int32 expo
, e
, ng
, tens
;
974 STBSP__COPYFP(bits
,d
);
975 expo
= (stbsp__int32
) ((bits
>> 52) & 2047);
976 ng
= (stbsp__int32
)(bits
>> 63);
979 if ( expo
== 2047 ) // is nan or inf?
981 *start
= (bits
&((((stbsp__uint64
)1)<<52)-1)) ? "NaN" : "Inf";
982 *decimal_pos
= STBSP__SPECIAL
;
987 if ( expo
== 0 ) // is zero or denormal
989 if ((bits
<<1)==0) // do zero
993 out
[0] = '0'; *len
= 1;
996 // find the right expo for denormals
998 stbsp__int64 v
= ((stbsp__uint64
)1)<<51;
999 while ((bits
&v
)==0) { --expo
; v
>>= 1; }
1003 // find the decimal exponent as well as the decimal bits of the value
1007 // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
1008 tens
=expo
-1023; tens
= (tens
<0)?((tens
*617)/2048):(((tens
*1233)/4096)+1);
1010 // move the significant bits into position and stick them into an int
1011 stbsp__raise_to_power10( &ph
, &pl
, d
, 18-tens
);
1013 // get full as much precision from double-double as possible
1014 stbsp__ddtoS64( bits
, ph
,pl
);
1016 // check if we undershot
1017 if ( ((stbsp__uint64
)bits
) >= stbsp__tento19th
) ++tens
;
1020 // now do the rounding in integer land
1021 frac_digits
= ( frac_digits
& 0x80000000 ) ? ( (frac_digits
&0x7ffffff) + 1 ) : ( tens
+ frac_digits
);
1022 if ( ( frac_digits
< 24 ) )
1024 stbsp__uint32 dg
= 1; if ((stbsp__uint64
)bits
>= stbsp__powten
[9] ) dg
=10; while( (stbsp__uint64
)bits
>= stbsp__powten
[dg
] ) { ++dg
; if (dg
==20) goto noround
; }
1025 if ( frac_digits
< dg
)
1028 // add 0.5 at the right position and round
1029 e
= dg
- frac_digits
;
1030 if ( (stbsp__uint32
)e
>= 24 ) goto noround
;
1031 r
= stbsp__powten
[e
];
1032 bits
= bits
+ (r
/2);
1033 if ( (stbsp__uint64
)bits
>= stbsp__powten
[dg
] ) ++tens
;
1039 // kill long trailing runs of zeros
1043 for(;;) { if ( bits
<=0xffffffff ) break; if (bits
%1000) goto donez
; bits
/=1000; }
1044 n
= (stbsp__uint32
)bits
;
1045 while ((n
%1000)==0) n
/=1000;
1050 // convert to string
1057 // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
1058 if (bits
>=100000000) { n
= (stbsp__uint32
)( bits
% 100000000); bits
/= 100000000; } else {n
= (stbsp__uint32
)bits
; bits
= 0; }
1059 while(n
) { out
-=2; *(stbsp__uint16
*)out
=*(stbsp__uint16
*)&stbsp__digitpair
[(n
%100)*2]; n
/=100; e
+=2; }
1060 if (bits
==0) { if ((e
) && (out
[0]=='0')) { ++out
; --e
; } break; }
1061 while( out
!=o
) { *--out
='0'; ++e
; }
1064 *decimal_pos
= tens
;
1070 #undef stbsp__ddmulthi
1071 #undef stbsp__ddrenorm
1072 #undef stbsp__ddmultlo
1073 #undef stbsp__ddmultlos
1074 #undef STBSP__SPECIAL
1075 #undef STBSP__COPYFP
1077 #endif // STB_SPRINTF_NOFLOAT
1080 #undef stbsp__uint16
1081 #undef stbsp__uint32
1083 #undef stbsp__uint64
1085 #undef STBSP__UNALIGNED
1087 #endif // STB_SPRINTF_IMPLEMENTATION