comments updated
[henge/apc.git] / stb / stb_sprintf.h
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
4 //
5 // allowed types: sc uidBboXx p AaGgEef n
6 // lengths : h ll j z t I64 I32 I
7 //
8 // Contributors (bugfixes):
9 // github:d26435
10 // github:trex78
11
12 #ifndef STB_SPRINTF_H_INCLUDE
13 #define STB_SPRINTF_H_INCLUDE
14
15 /*
16 Single file sprintf replacement.
17
18 Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
19 Hereby placed in public domain.
20
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.
24
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.
32
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.
37
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.
41
42 It compiles to roughly 8K with float support, and 4K without.
43 As a comparison, when using MSVC static libs, calling sprintf drags
44 in 16K.
45
46 API:
47 ====
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).
52
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).
57
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
65
66 void stbsp_set_separators( char comma, char period )
67 Set the comma and period characters to use.
68
69 FLOATS/DOUBLES:
70 ===============
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.
75
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
79 doesn't either).
80
81 If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
82 and you'll save 4K of code space.
83
84 64-BIT INTS:
85 ============
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.
89
90 EXTRAS:
91 =======
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
94 would print 12,345.
95
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
99 "2.42 m", etc.
100
101 In addition to octal and hexadecimal conversions, you can print
102 integers in binary: "%b" for 256 would print 100.
103
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!)
123 */
124
125 #if defined(__has_feature)
126 #if __has_feature(address_sanitizer)
127 #define STBI__ASAN __attribute__((no_sanitize("address")))
128 #endif
129 #endif
130 #ifndef STBI__ASAN
131 #define STBI__ASAN
132 #endif
133
134 #ifdef STB_SPRINTF_STATIC
135 #define STBSP__PUBLICDEC static
136 #define STBSP__PUBLICDEF static STBI__ASAN
137 #else
138 #ifdef __cplusplus
139 #define STBSP__PUBLICDEC extern "C"
140 #define STBSP__PUBLICDEF extern "C" STBI__ASAN
141 #else
142 #define STBSP__PUBLICDEC extern
143 #define STBSP__PUBLICDEF STBI__ASAN
144 #endif
145 #endif
146
147 #include <stdarg.h> // for va_list()
148
149 #ifndef STB_SPRINTF_MIN
150 #define STB_SPRINTF_MIN 512 // how many characters per callback
151 #endif
152 typedef char * STBSP_SPRINTFCB( char * buf, void * user, int len );
153
154 #ifndef STB_SPRINTF_DECORATE
155 #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
156 #endif
157
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, ... );
162
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 );
165
166 #endif // STB_SPRINTF_H_INCLUDE
167
168 #ifdef STB_SPRINTF_IMPLEMENTATION
169
170 #include <stdlib.h> // for va_arg()
171
172 #define stbsp__uint32 unsigned int
173 #define stbsp__int32 signed int
174
175 #ifdef _MSC_VER
176 #define stbsp__uint64 unsigned __int64
177 #define stbsp__int64 signed __int64
178 #else
179 #define stbsp__uint64 unsigned long long
180 #define stbsp__int64 signed long long
181 #endif
182 #define stbsp__uint16 unsigned short
183
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
187 #else
188 #define stbsp__uintptr stbsp__uint32
189 #endif
190 #endif
191
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
195 #endif
196 #endif
197
198 #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
199 #define STBSP__UNALIGNED(code)
200 #else
201 #define STBSP__UNALIGNED(code) code
202 #endif
203
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
209 #endif
210
211 static char stbsp__period='.';
212 static char stbsp__comma=',';
213 static char stbsp__digitpair[201]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
214
215 STBSP__PUBLICDEF void STB_SPRINTF_DECORATE( set_separators )( char pcomma, char pperiod )
216 {
217 stbsp__period=pperiod;
218 stbsp__comma=pcomma;
219 }
220
221 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsprintfcb )( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
222 {
223 static char hex[]="0123456789abcdefxp";
224 static char hexu[]="0123456789ABCDEFXP";
225 char * bf;
226 char const * f;
227 int tlen = 0;
228
229 bf = buf;
230 f = fmt;
231 for(;;)
232 {
233 stbsp__int32 fw,pr,tz; stbsp__uint32 fl;
234
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
245
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; }
251
252 // fast copy everything up to the next % (or end of string)
253 for(;;)
254 {
255 while (((stbsp__uintptr)f)&3)
256 {
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;
260 }
261 for(;;)
262 {
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
266 stbsp__uint32 v,c;
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;
272 }
273 } scandd:
274
275 ++f;
276
277 // ok, we have a percent, read the modifiers first
278 fw = 0; pr = -1; fl = 0; tz = 0;
279
280 // flags
281 for(;;)
282 {
283 switch(f[0])
284 {
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;
300 }
301 }
302 flags_done:
303
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++; } }
306 // get the precision
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++; } } }
308
309 // handle integer size overrides
310 switch(f[0])
311 {
312 // are we halfwidth?
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;
324 default: break;
325 }
326
327 // handle each replacement
328 switch( f[0] )
329 {
330 #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
331 char num[STBSP__NUMSZ];
332 char lead[8];
333 char tail[8];
334 char *s;
335 char const *h;
336 stbsp__uint32 l,n,cs;
337 stbsp__uint64 n64;
338 #ifndef STB_SPRINTF_NOFLOAT
339 double fv;
340 #endif
341 stbsp__int32 dp; char const * sn;
342
343 case 's':
344 // get the string
345 s = va_arg(va,char*); if (s==0) s = (char*)"null";
346 // get the length
347 sn = s;
348 for(;;)
349 {
350 if ((((stbsp__uintptr)sn)&3)==0) break;
351 lchk:
352 if (sn[0]==0) goto ld;
353 ++sn;
354 }
355 n = 0xffffffff;
356 if (pr>=0) { n=(stbsp__uint32)(sn-s); if (n>=(stbsp__uint32)pr) goto ld; n=((stbsp__uint32)(pr-n))>>2; }
357 while(n)
358 {
359 stbsp__uint32 v=*(stbsp__uint32*)sn;
360 if ((v-0x01010101)&(~v)&0x80808080UL) goto lchk;
361 sn+=4;
362 --n;
363 }
364 goto lchk;
365 ld:
366
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
372 goto scopy;
373
374 case 'c': // char
375 // get the character
376 s = num + STBSP__NUMSZ -1; *s = (char)va_arg(va,int);
377 l = 1;
378 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
379 goto scopy;
380
381 case 'n': // weird write-bytes specifier
382 { int * d = va_arg(va,int*);
383 *d = tlen + (int)( bf - buf ); }
384 break;
385
386 #ifdef STB_SPRINTF_NOFLOAT
387 case 'A': // float
388 case 'a': // hex float
389 case 'G': // float
390 case 'g': // float
391 case 'E': // float
392 case 'e': // float
393 case 'f': // float
394 va_arg(va,double); // eat it
395 s = (char*)"No float";
396 l = 8;
397 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
398 goto scopy;
399 #else
400 case 'A': // float
401 h=hexu;
402 goto hexfloat;
403
404 case 'a': // hex float
405 h=hex;
406 hexfloat:
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;
412
413 s = num+64;
414
415 // sign
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]='+'; };
417
418 if (dp==-1023) dp=(n64)?-1022:0; else n64|=(((stbsp__uint64)1)<<52);
419 n64<<=(64-56);
420 if (pr<15) n64+=((((stbsp__uint64)8)<<56)>>(pr*4));
421 // add leading chars
422
423 #ifdef STB_SPRINTF_MSVC_MODE
424 *s++='0';*s++='x';
425 #else
426 lead[1+lead[0]]='0'; lead[2+lead[0]]='x'; lead[0]+=2;
427 #endif
428 *s++=h[(n64>>60)&15]; n64<<=4;
429 if ( pr ) *s++=stbsp__period;
430 sn = s;
431
432 // print the bits
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; }
435
436 // print the expo
437 tail[1]=h[17];
438 if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
439 n = (dp>=1000)?6:((dp>=100)?5:((dp>=10)?4:3));
440 tail[0]=(char)n;
441 for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
442
443 dp = (int)(s-sn);
444 l = (int)(s-(num+64));
445 s = num+64;
446 cs = 1 + (3<<24);
447 goto scopy;
448
449 case 'G': // float
450 h=hexu;
451 goto dosmallfloat;
452
453 case 'g': // float
454 h=hex;
455 dosmallfloat:
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;
461
462 // clamp the precision and delete extra zeros after clamp
463 n = pr;
464 if ( l > (stbsp__uint32)pr ) l = pr; while ((l>1)&&(pr)&&(sn[l-1]=='0')) { --pr; --l; }
465
466 // should we use %e
467 if ((dp<=-4)||(dp>(stbsp__int32)n))
468 {
469 if ( pr > (stbsp__int32)l ) pr = l-1; else if ( pr ) --pr; // when using %e, there is one digit before the decimal
470 goto doexpfromg;
471 }
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); }
474 goto dofloatfromg;
475
476 case 'E': // float
477 h=hexu;
478 goto doexp;
479
480 case 'e': // float
481 h=hex;
482 doexp:
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;
488 doexpfromg:
489 tail[0]=0;
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; }
492 s=num+64;
493 // handle leading chars
494 *s++=sn[0];
495
496 if (pr) *s++=stbsp__period;
497
498 // handle after decimal
499 if ((l-1)>(stbsp__uint32)pr) l=pr+1;
500 for(n=1;n<l;n++) *s++=sn[n];
501 // trailing zeros
502 tz = pr-(l-1); pr=0;
503 // dump expo
504 tail[1]=h[0xe];
505 dp -= 1;
506 if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
507 #ifdef STB_SPRINTF_MSVC_MODE
508 n = 5;
509 #else
510 n = (dp>=100)?5:4;
511 #endif
512 tail[0]=(char)n;
513 for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
514 cs = 1 + (3<<24); // how many tens
515 goto flt_lead;
516
517 case 'f': // float
518 fv = va_arg(va,double);
519 doafloat:
520 // do kilos
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;
526 dofloatfromg:
527 tail[0]=0;
528 // sign
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; }
531 s=num+64;
532
533 // handle the three decimal varieties
534 if (dp<=0)
535 {
536 stbsp__int32 i;
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; }
541 tz = pr-(n+l);
542 cs = 1 + (3<<24); // how many tens did we write (for commas below)
543 }
544 else
545 {
546 cs = (fl&STBSP__TRIPLET_COMMA)?((600-(stbsp__uint32)dp)%3):0;
547 if ((stbsp__uint32)dp>=l)
548 {
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)
552 {
553 n = dp - n;
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; } }
556 }
557 cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
558 if (pr) { *s++=stbsp__period; tz=pr;}
559 }
560 else
561 {
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; }
568 tz = pr-(l-dp);
569 }
570 }
571 pr = 0;
572
573 // handle k,m,g,t
574 if (fl&STBSP__METRIC_SUFFIX) { tail[0]=1; tail[1]=' '; { if (fl>>24) { tail[2]="_kmgt"[fl>>24]; tail[0]=2; } } };
575
576 flt_lead:
577 // get the length that we copied
578 l = (stbsp__uint32) ( s-(num+64) );
579 s=num+64;
580 goto scopy;
581 #endif
582
583 case 'B': // upper binary
584 h = hexu;
585 goto binary;
586
587 case 'b': // lower binary
588 h = hex;
589 binary:
590 lead[0]=0;
591 if (fl&STBSP__LEADING_0X) { lead[0]=2;lead[1]='0';lead[2]=h[0xb]; }
592 l=(8<<4)|(1<<8);
593 goto radixnum;
594
595 case 'o': // octal
596 h = hexu;
597 lead[0]=0;
598 if (fl&STBSP__LEADING_0X) { lead[0]=1;lead[1]='0'; }
599 l=(3<<4)|(3<<8);
600 goto radixnum;
601
602 case 'p': // pointer
603 fl |= (sizeof(void*)==8)?STBSP__INTMAX:0;
604 pr = sizeof(void*)*2;
605 fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
606 // drop through to X
607
608 case 'X': // upper binary
609 h = hexu;
610 goto dohexb;
611
612 case 'x': // lower binary
613 h = hex; dohexb:
614 l=(4<<4)|(4<<8);
615 lead[0]=0;
616 if (fl&STBSP__LEADING_0X) { lead[0]=2;lead[1]='0';lead[2]=h[16]; }
617 radixnum:
618 // get the number
619 if ( fl&STBSP__INTMAX )
620 n64 = va_arg(va,stbsp__uint64);
621 else
622 n64 = va_arg(va,stbsp__uint32);
623
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; } }
627 // convert to string
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 );
633 // copy it
634 goto scopy;
635
636 case 'u': // unsigned
637 case 'i':
638 case 'd': // integer
639 // get the integer and abs it
640 if ( fl&STBSP__INTMAX )
641 {
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; }
643 }
644 else
645 {
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; }
647 }
648
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; }
651 #endif
652
653 // convert to string
654 s = num+STBSP__NUMSZ; l=0;
655
656 for(;;)
657 {
658 // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
659 char * o=s-8;
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'; }
665 }
666
667 tail[0]=0;
668 // sign
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]='+'; };
670
671 // get the length that we copied
672 l = (stbsp__uint32) ( (num+STBSP__NUMSZ) - s ); if ( l == 0 ) { *--s='0'; l = 1; }
673 cs = l + (3<<24);
674 if (pr<0) pr = 0;
675
676 scopy:
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;
681 fw -= n;
682 pr -= l;
683
684 // handle right justify and leading zeros
685 if ( (fl&STBSP__LEFTJUST)==0 )
686 {
687 if (fl&STBSP__LEADINGZERO) // if leading zeros, everything is in pr
688 {
689 pr = (fw>pr)?fw:pr;
690 fw = 0;
691 }
692 else
693 {
694 fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
695 }
696 }
697
698 // copy the spaces and/or zeros
699 if (fw+pr)
700 {
701 stbsp__int32 i; stbsp__uint32 c;
702
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); }
705
706 // copy leader
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); }
708
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); }
713 }
714
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); }
717
718 // copy the string
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); }
720
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); }
723
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); }
726
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); } }
729 break;
730
731 default: // unknown, just copy code
732 s = num + STBSP__NUMSZ -1; *s = f[0];
733 l = 1;
734 fw=pr=fl=0;
735 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
736 goto scopy;
737 }
738 ++f;
739 }
740 endfmt:
741
742 if (!callback)
743 *bf = 0;
744 else
745 stbsp__flush_cb();
746
747 done:
748 return tlen + (int)(bf-buf);
749 }
750
751 // cleanup
752 #undef STBSP__LEFTJUST
753 #undef STBSP__LEADINGPLUS
754 #undef STBSP__LEADINGSPACE
755 #undef STBSP__LEADING_0X
756 #undef STBSP__LEADINGZERO
757 #undef STBSP__INTMAX
758 #undef STBSP__TRIPLET_COMMA
759 #undef STBSP__NEGATIVE
760 #undef STBSP__METRIC_SUFFIX
761 #undef STBSP__NUMSZ
762 #undef stbsp__chk_cb_bufL
763 #undef stbsp__chk_cb_buf
764 #undef stbsp__flush_cb
765 #undef stbsp__cb_buf_clamp
766
767 // ============================================================================
768 // wrapper functions
769
770 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( sprintf )( char * buf, char const * fmt, ... )
771 {
772 int result;
773 va_list va;
774 va_start( va, fmt );
775 result = STB_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
776 va_end(va);
777 return result;
778 }
779
780 typedef struct stbsp__context
781 {
782 char * buf;
783 int count;
784 char tmp[ STB_SPRINTF_MIN ];
785 } stbsp__context;
786
787 static char * stbsp__clamp_callback( char * buf, void * user, int len )
788 {
789 stbsp__context * c = (stbsp__context*)user;
790
791 if ( len > c->count ) len = c->count;
792
793 if (len)
794 {
795 if ( buf != c->buf )
796 {
797 char * s, * d, * se;
798 d = c->buf; s = buf; se = buf+len;
799 do{ *d++ = *s++; } while (s<se);
800 }
801 c->buf += len;
802 c->count -= len;
803 }
804
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
807 }
808
809 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
810 {
811 stbsp__context c;
812 int l;
813
814 if ( count == 0 )
815 return 0;
816
817 c.buf = buf;
818 c.count = count;
819
820 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );
821
822 // zero-terminate
823 l = (int)( c.buf - buf );
824 if ( l >= count ) // should never be greater, only equal (or less) than count
825 l = count - 1;
826 buf[l] = 0;
827
828 return l;
829 }
830
831 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... )
832 {
833 int result;
834 va_list va;
835 va_start( va, fmt );
836
837 result = STB_SPRINTF_DECORATE( vsnprintf )( buf, count, fmt, va );
838 va_end(va);
839
840 return result;
841 }
842
843 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va )
844 {
845 return STB_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
846 }
847
848 // =======================================================================
849 // low level float utility functions
850
851 #ifndef STB_SPRINTF_NOFLOAT
852
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]; }
855
856 // get float info
857 static stbsp__int32 stbsp__real_to_parts( stbsp__int64 * bits, stbsp__int32 * expo, double value )
858 {
859 double d;
860 stbsp__int64 b = 0;
861
862 // load value and round at the frac_digits
863 d = value;
864
865 STBSP__COPYFP( b, d );
866
867 *bits = b & ((((stbsp__uint64)1)<<52)-1);
868 *expo = (stbsp__int32) (((b >> 52) & 2047)-1023);
869
870 return (stbsp__int32)(b >> 63);
871 }
872
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};
880
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)
884 #else
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)
887 #endif
888
889 #define stbsp__ddmulthi(oh,ol,xh,yh) \
890 { \
891 double ahi=0,alo,bhi=0,blo; \
892 stbsp__int64 bt; \
893 oh = xh * yh; \
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; \
897 }
898
899 #define stbsp__ddtoS64(ob,xh,xl) \
900 { \
901 double ahi=0,alo,vh,t;\
902 ob = (stbsp__int64)ph;\
903 vh=(double)ob;\
904 ahi = ( xh - vh );\
905 t = ( ahi - xh );\
906 alo = (xh-(ahi-t))-(vh+t);\
907 ob += (stbsp__int64)(ahi+alo+xl);\
908 }
909
910
911 #define stbsp__ddrenorm(oh,ol) { double s; s=oh+ol; ol=ol-(s-oh); oh=s; }
912
913 #define stbsp__ddmultlo(oh,ol,xh,xl,yh,yl) \
914 ol = ol + ( xh*yl + xl*yh ); \
915
916 #define stbsp__ddmultlos(oh,ol,xh,yl) \
917 ol = ol + ( xh*yl ); \
918
919 static void stbsp__raise_to_power10( double *ohi, double *olo, double d, stbsp__int32 power ) // power can be -323 to +350
920 {
921 double ph, pl;
922 if ((power>=0) && (power<=22))
923 {
924 stbsp__ddmulthi(ph,pl,d,stbsp__bot[power]);
925 }
926 else
927 {
928 stbsp__int32 e,et,eb;
929 double p2h,p2l;
930
931 e=power; if (power<0) e=-e;
932 et = (e*0x2c9)>>14;/* %23 */ if (et>13) et=13; eb = e-(et*23);
933
934 ph = d; pl = 0.0;
935 if (power<0)
936 {
937 if (eb) { --eb; stbsp__ddmulthi(ph,pl,d,stbsp__negbot[eb]); stbsp__ddmultlos(ph,pl,d,stbsp__negboterr[eb]); }
938 if (et)
939 {
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;
942 }
943 }
944 else
945 {
946 if (eb)
947 {
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; }
951 }
952 if (et)
953 {
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;
956 }
957 }
958 }
959 stbsp__ddrenorm(ph,pl);
960 *ohi = ph; *olo = pl;
961 }
962
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 )
968 {
969 double d;
970 stbsp__int64 bits = 0;
971 stbsp__int32 expo, e, ng, tens;
972
973 d = value;
974 STBSP__COPYFP(bits,d);
975 expo = (stbsp__int32) ((bits >> 52) & 2047);
976 ng = (stbsp__int32)(bits >> 63);
977 if (ng) d=-d;
978
979 if ( expo == 2047 ) // is nan or inf?
980 {
981 *start = (bits&((((stbsp__uint64)1)<<52)-1)) ? "NaN" : "Inf";
982 *decimal_pos = STBSP__SPECIAL;
983 *len = 3;
984 return ng;
985 }
986
987 if ( expo == 0 ) // is zero or denormal
988 {
989 if ((bits<<1)==0) // do zero
990 {
991 *decimal_pos = 1;
992 *start = out;
993 out[0] = '0'; *len = 1;
994 return ng;
995 }
996 // find the right expo for denormals
997 {
998 stbsp__int64 v = ((stbsp__uint64)1)<<51;
999 while ((bits&v)==0) { --expo; v >>= 1; }
1000 }
1001 }
1002
1003 // find the decimal exponent as well as the decimal bits of the value
1004 {
1005 double ph,pl;
1006
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);
1009
1010 // move the significant bits into position and stick them into an int
1011 stbsp__raise_to_power10( &ph, &pl, d, 18-tens );
1012
1013 // get full as much precision from double-double as possible
1014 stbsp__ddtoS64( bits, ph,pl );
1015
1016 // check if we undershot
1017 if ( ((stbsp__uint64)bits) >= stbsp__tento19th ) ++tens;
1018 }
1019
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 ) )
1023 {
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 )
1026 {
1027 stbsp__uint64 r;
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;
1034 bits /= r;
1035 }
1036 noround:;
1037 }
1038
1039 // kill long trailing runs of zeros
1040 if ( bits )
1041 {
1042 stbsp__uint32 n;
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;
1046 bits=n;
1047 donez:;
1048 }
1049
1050 // convert to string
1051 out += 64;
1052 e = 0;
1053 for(;;)
1054 {
1055 stbsp__uint32 n;
1056 char * o = out-8;
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; }
1062 }
1063
1064 *decimal_pos = tens;
1065 *start = out;
1066 *len = e;
1067 return ng;
1068 }
1069
1070 #undef stbsp__ddmulthi
1071 #undef stbsp__ddrenorm
1072 #undef stbsp__ddmultlo
1073 #undef stbsp__ddmultlos
1074 #undef STBSP__SPECIAL
1075 #undef STBSP__COPYFP
1076
1077 #endif // STB_SPRINTF_NOFLOAT
1078
1079 // clean up
1080 #undef stbsp__uint16
1081 #undef stbsp__uint32
1082 #undef stbsp__int32
1083 #undef stbsp__uint64
1084 #undef stbsp__int64
1085 #undef STBSP__UNALIGNED
1086
1087 #endif // STB_SPRINTF_IMPLEMENTATION