Path: blob/a-new-beginning/SharedDependencies/Sources/microprofile/include/stb_sprintf.h
2 views
// stb_sprintf - v1.10 - public domain snprintf() implementation1// originally by Jeff Roberts / RAD Game Tools, 2015/10/202// http://github.com/nothings/stb3//4// allowed types: sc uidBboXx p AaGgEef n5// lengths : hh h ll j z t I64 I32 I6//7// Contributors:8// Fabian "ryg" Giesen (reformatting)9// github:aganm (attribute format)10//11// Contributors (bugfixes):12// github:d2643513// github:trex7814// github:account-login15// Jari Komppa (SI suffixes)16// Rohit Nirmal17// Marcin Wojdyr18// Leonard Ritter19// Stefano Zanotti20// Adam Allison21// Arvid Gerstmann22// Markus Kolb23//24// LICENSE:25//26// See end of file for license information.2728#ifndef STB_SPRINTF_H_INCLUDE29#define STB_SPRINTF_H_INCLUDE3031/*32Single file sprintf replacement.3334Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.35Hereby placed in public domain.3637This is a full sprintf replacement that supports everything that38the C runtime sprintfs support, including float/double, 64-bit integers,39hex floats, field parameters (%*.*d stuff), length reads backs, etc.4041Why would you need this if sprintf already exists? Well, first off,42it's *much* faster (see below). It's also much smaller than the CRT43versions code-space-wise. We've also added some simple improvements44that are super handy (commas in thousands, callbacks at buffer full,45for example). Finally, the format strings for MSVC and GCC differ46for 64-bit integers (among other small things), so this lets you use47the same format strings in cross platform code.4849It uses the standard single file trick of being both the header file50and the source itself. If you just include it normally, you just get51the header file function definitions. To get the code, you include52it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.5354It only uses va_args macros from the C runtime to do it's work. It55does cast doubles to S64s and shifts and divides U64s, which does56drag in CRT code on most platforms.5758It compiles to roughly 8K with float support, and 4K without.59As a comparison, when using MSVC static libs, calling sprintf drags60in 16K.6162API:63====64int stbsp_sprintf( char * buf, char const * fmt, ... )65int stbsp_snprintf( char * buf, int count, char const * fmt, ... )66Convert an arg list into a buffer. stbsp_snprintf always returns67a zero-terminated string (unlike regular snprintf).6869int stbsp_vsprintf( char * buf, char const * fmt, va_list va )70int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )71Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns72a zero-terminated string (unlike regular snprintf).7374int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )75typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );76Convert into a buffer, calling back every STB_SPRINTF_MIN chars.77Your callback can then copy the chars out, print them or whatever.78This function is actually the workhorse for everything else.79The buffer you pass in must hold at least STB_SPRINTF_MIN characters.80// you return the next buffer to use or 0 to stop converting8182void stbsp_set_separators( char comma, char period )83Set the comma and period characters to use.8485FLOATS/DOUBLES:86===============87This code uses a internal float->ascii conversion method that uses88doubles with error correction (double-doubles, for ~105 bits of89precision). This conversion is round-trip perfect - that is, an atof90of the values output here will give you the bit-exact double back.9192One difference is that our insignificant digits will be different than93with MSVC or GCC (but they don't match each other either). We also94don't attempt to find the minimum length matching float (pre-MSVC1595doesn't either).9697If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT98and you'll save 4K of code space.9910064-BIT INTS:101============102This library also supports 64-bit integers and you can use MSVC style or103GCC style indicators (%I64d or %lld). It supports the C99 specifiers104for size_t and ptr_diff_t (%jd %zd) as well.105106EXTRAS:107=======108Like some GCCs, for integers and floats, you can use a ' (single quote)109specifier and commas will be inserted on the thousands: "%'d" on 12345110would print 12,345.111112For integers and floats, you can use a "$" specifier and the number113will be converted to float and then divided to get kilo, mega, giga or114tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is115"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn1162536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three117$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the118suffix, add "_" specifier: "%_$d" -> "2.53M".119120In addition to octal and hexadecimal conversions, you can print121integers in binary: "%b" for 256 would print 100.122123PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):124===================================================================125"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)126"%24d" across all 32-bit ints (4.5x/4.2x faster)127"%x" across all 32-bit ints (4.5x/3.8x faster)128"%08x" across all 32-bit ints (4.3x/3.8x faster)129"%f" across e-10 to e+10 floats (7.3x/6.0x faster)130"%e" across e-10 to e+10 floats (8.1x/6.0x faster)131"%g" across e-10 to e+10 floats (10.0x/7.1x faster)132"%f" for values near e-300 (7.9x/6.5x faster)133"%f" for values near e+300 (10.0x/9.1x faster)134"%e" for values near e-300 (10.1x/7.0x faster)135"%e" for values near e+300 (9.2x/6.0x faster)136"%.320f" for values near e-300 (12.6x/11.2x faster)137"%a" for random values (8.6x/4.3x faster)138"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)139"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)140"%s%s%s" for 64 char strings (7.1x/7.3x faster)141"...512 char string..." ( 35.0x/32.5x faster!)142*/143144#if defined(__clang__)145#if defined(__has_feature) && defined(__has_attribute)146#if __has_feature(address_sanitizer)147#if __has_attribute(__no_sanitize__)148#define STBSP__ASAN __attribute__((__no_sanitize__("address")))149#elif __has_attribute(__no_sanitize_address__)150#define STBSP__ASAN __attribute__((__no_sanitize_address__))151#elif __has_attribute(__no_address_safety_analysis__)152#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))153#endif154#endif155#endif156#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))157#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__158#define STBSP__ASAN __attribute__((__no_sanitize_address__))159#endif160#endif161162#ifndef STBSP__ASAN163#define STBSP__ASAN164#endif165166#ifdef STB_SPRINTF_STATIC167#define STBSP__PUBLICDEC static168#define STBSP__PUBLICDEF static STBSP__ASAN169#else170#ifdef __cplusplus171#define STBSP__PUBLICDEC extern "C"172#define STBSP__PUBLICDEF extern "C" STBSP__ASAN173#else174#define STBSP__PUBLICDEC extern175#define STBSP__PUBLICDEF STBSP__ASAN176#endif177#endif178179#if defined(__has_attribute)180#if __has_attribute(format)181#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))182#endif183#endif184185#ifndef STBSP__ATTRIBUTE_FORMAT186#define STBSP__ATTRIBUTE_FORMAT(fmt,va)187#endif188189#ifdef _MSC_VER190#define STBSP__NOTUSED(v) (void)(v)191#else192#define STBSP__NOTUSED(v) (void)sizeof(v)193#endif194195#include <stdarg.h> // for va_arg(), va_list()196#include <stddef.h> // size_t, ptrdiff_t197198#ifndef STB_SPRINTF_MIN199#define STB_SPRINTF_MIN 512 // how many characters per callback200#endif201typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);202203#ifndef STB_SPRINTF_DECORATE204#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names205#endif206207STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);208STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);209STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);210STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);211212STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);213STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);214215#endif // STB_SPRINTF_H_INCLUDE216217#ifdef STB_SPRINTF_IMPLEMENTATION218219#define stbsp__uint32 unsigned int220#define stbsp__int32 signed int221222#ifdef _MSC_VER223#define stbsp__uint64 unsigned __int64224#define stbsp__int64 signed __int64225#else226#define stbsp__uint64 unsigned long long227#define stbsp__int64 signed long long228#endif229#define stbsp__uint16 unsigned short230231#ifndef stbsp__uintptr232#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__)233#define stbsp__uintptr stbsp__uint64234#else235#define stbsp__uintptr stbsp__uint32236#endif237#endif238239#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)240#if defined(_MSC_VER) && (_MSC_VER < 1900)241#define STB_SPRINTF_MSVC_MODE242#endif243#endif244245#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses246#define STBSP__UNALIGNED(code)247#else248#define STBSP__UNALIGNED(code) code249#endif250251#ifndef STB_SPRINTF_NOFLOAT252// internal float utility functions253static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);254static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);255#define STBSP__SPECIAL 0x7000256#endif257258static char stbsp__period = '.';259static char stbsp__comma = ',';260static struct261{262short temp; // force next field to be 2-byte aligned263char pair[201];264} stbsp__digitpair =265{2660,267"00010203040506070809101112131415161718192021222324"268"25262728293031323334353637383940414243444546474849"269"50515253545556575859606162636465666768697071727374"270"75767778798081828384858687888990919293949596979899"271};272273STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod)274{275stbsp__period = pperiod;276stbsp__comma = pcomma;277}278279#define STBSP__LEFTJUST 1280#define STBSP__LEADINGPLUS 2281#define STBSP__LEADINGSPACE 4282#define STBSP__LEADING_0X 8283#define STBSP__LEADINGZERO 16284#define STBSP__INTMAX 32285#define STBSP__TRIPLET_COMMA 64286#define STBSP__NEGATIVE 128287#define STBSP__METRIC_SUFFIX 256288#define STBSP__HALFWIDTH 512289#define STBSP__METRIC_NOSPACE 1024290#define STBSP__METRIC_1024 2048291#define STBSP__METRIC_JEDEC 4096292293static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)294{295sign[0] = 0;296if (fl & STBSP__NEGATIVE) {297sign[0] = 1;298sign[1] = '-';299} else if (fl & STBSP__LEADINGSPACE) {300sign[0] = 1;301sign[1] = ' ';302} else if (fl & STBSP__LEADINGPLUS) {303sign[0] = 1;304sign[1] = '+';305}306}307308static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit)309{310char const * sn = s;311312// get up to 4-byte alignment313for (;;) {314if (((stbsp__uintptr)sn & 3) == 0)315break;316317if (!limit || *sn == 0)318return (stbsp__uint32)(sn - s);319320++sn;321--limit;322}323324// scan over 4 bytes at a time to find terminating 0325// this will intentionally scan up to 3 bytes past the end of buffers,326// but becase it works 4B aligned, it will never cross page boundaries327// (hence the STBSP__ASAN markup; the over-read here is intentional328// and harmless)329while (limit >= 4) {330stbsp__uint32 v = *(stbsp__uint32 *)sn;331// bit hack to find if there's a 0 byte in there332if ((v - 0x01010101) & (~v) & 0x80808080UL)333break;334335sn += 4;336limit -= 4;337}338339// handle the last few characters to find actual size340while (limit && *sn) {341++sn;342--limit;343}344345return (stbsp__uint32)(sn - s);346}347348STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)349{350static char hex[] = "0123456789abcdefxp";351static char hexu[] = "0123456789ABCDEFXP";352char *bf;353char const *f;354int tlen = 0;355356bf = buf;357f = fmt;358for (;;) {359stbsp__int32 fw, pr, tz;360stbsp__uint32 fl;361362// macros for the callback buffer stuff363#define stbsp__chk_cb_bufL(bytes) \364{ \365int len = (int)(bf - buf); \366if ((len + (bytes)) >= STB_SPRINTF_MIN) { \367tlen += len; \368if (0 == (bf = buf = callback(buf, user, len))) \369goto done; \370} \371}372#define stbsp__chk_cb_buf(bytes) \373{ \374if (callback) { \375stbsp__chk_cb_bufL(bytes); \376} \377}378#define stbsp__flush_cb() \379{ \380stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \381} // flush if there is even one byte in the buffer382#define stbsp__cb_buf_clamp(cl, v) \383cl = v; \384if (callback) { \385int lg = STB_SPRINTF_MIN - (int)(bf - buf); \386if (cl > lg) \387cl = lg; \388}389390// fast copy everything up to the next % (or end of string)391for (;;) {392while (((stbsp__uintptr)f) & 3) {393schk1:394if (f[0] == '%')395goto scandd;396schk2:397if (f[0] == 0)398goto endfmt;399stbsp__chk_cb_buf(1);400*bf++ = f[0];401++f;402}403for (;;) {404// Check if the next 4 bytes contain %(0x25) or end of string.405// Using the 'hasless' trick:406// https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord407stbsp__uint32 v, c;408v = *(stbsp__uint32 *)f;409c = (~v) & 0x80808080;410if (((v ^ 0x25252525) - 0x01010101) & c)411goto schk1;412if ((v - 0x01010101) & c)413goto schk2;414if (callback)415if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4)416goto schk1;417#ifdef STB_SPRINTF_NOUNALIGNED418if(((stbsp__uintptr)bf) & 3) {419bf[0] = f[0];420bf[1] = f[1];421bf[2] = f[2];422bf[3] = f[3];423} else424#endif425{426*(stbsp__uint32 *)bf = v;427}428bf += 4;429f += 4;430}431}432scandd:433434++f;435436// ok, we have a percent, read the modifiers first437fw = 0;438pr = -1;439fl = 0;440tz = 0;441442// flags443for (;;) {444switch (f[0]) {445// if we have left justify446case '-':447fl |= STBSP__LEFTJUST;448++f;449continue;450// if we have leading plus451case '+':452fl |= STBSP__LEADINGPLUS;453++f;454continue;455// if we have leading space456case ' ':457fl |= STBSP__LEADINGSPACE;458++f;459continue;460// if we have leading 0x461case '#':462fl |= STBSP__LEADING_0X;463++f;464continue;465// if we have thousand commas466case '\'':467fl |= STBSP__TRIPLET_COMMA;468++f;469continue;470// if we have kilo marker (none->kilo->kibi->jedec)471case '$':472if (fl & STBSP__METRIC_SUFFIX) {473if (fl & STBSP__METRIC_1024) {474fl |= STBSP__METRIC_JEDEC;475} else {476fl |= STBSP__METRIC_1024;477}478} else {479fl |= STBSP__METRIC_SUFFIX;480}481++f;482continue;483// if we don't want space between metric suffix and number484case '_':485fl |= STBSP__METRIC_NOSPACE;486++f;487continue;488// if we have leading zero489case '0':490fl |= STBSP__LEADINGZERO;491++f;492goto flags_done;493default: goto flags_done;494}495}496flags_done:497498// get the field width499if (f[0] == '*') {500fw = va_arg(va, stbsp__uint32);501++f;502} else {503while ((f[0] >= '0') && (f[0] <= '9')) {504fw = fw * 10 + f[0] - '0';505f++;506}507}508// get the precision509if (f[0] == '.') {510++f;511if (f[0] == '*') {512pr = va_arg(va, stbsp__uint32);513++f;514} else {515pr = 0;516while ((f[0] >= '0') && (f[0] <= '9')) {517pr = pr * 10 + f[0] - '0';518f++;519}520}521}522523// handle integer size overrides524switch (f[0]) {525// are we halfwidth?526case 'h':527fl |= STBSP__HALFWIDTH;528++f;529if (f[0] == 'h')530++f; // QUARTERWIDTH531break;532// are we 64-bit (unix style)533case 'l':534fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0);535++f;536if (f[0] == 'l') {537fl |= STBSP__INTMAX;538++f;539}540break;541// are we 64-bit on intmax? (c99)542case 'j':543fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0;544++f;545break;546// are we 64-bit on size_t or ptrdiff_t? (c99)547case 'z':548fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;549++f;550break;551case 't':552fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;553++f;554break;555// are we 64-bit (msft style)556case 'I':557if ((f[1] == '6') && (f[2] == '4')) {558fl |= STBSP__INTMAX;559f += 3;560} else if ((f[1] == '3') && (f[2] == '2')) {561f += 3;562} else {563fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0);564++f;565}566break;567default: break;568}569570// handle each replacement571switch (f[0]) {572#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307573char num[STBSP__NUMSZ];574char lead[8];575char tail[8];576char *s;577char const *h;578stbsp__uint32 l, n, cs;579stbsp__uint64 n64;580#ifndef STB_SPRINTF_NOFLOAT581double fv;582#endif583stbsp__int32 dp;584char const *sn;585586case 's':587// get the string588s = va_arg(va, char *);589if (s == 0)590s = (char *)"null";591// get the length, limited to desired precision592// always limit to ~0u chars since our counts are 32b593l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u);594lead[0] = 0;595tail[0] = 0;596pr = 0;597dp = 0;598cs = 0;599// copy the string in600goto scopy;601602case 'c': // char603// get the character604s = num + STBSP__NUMSZ - 1;605*s = (char)va_arg(va, int);606l = 1;607lead[0] = 0;608tail[0] = 0;609pr = 0;610dp = 0;611cs = 0;612goto scopy;613614case 'n': // weird write-bytes specifier615{616int *d = va_arg(va, int *);617*d = tlen + (int)(bf - buf);618} break;619620#ifdef STB_SPRINTF_NOFLOAT621case 'A': // float622case 'a': // hex float623case 'G': // float624case 'g': // float625case 'E': // float626case 'e': // float627case 'f': // float628va_arg(va, double); // eat it629s = (char *)"No float";630l = 8;631lead[0] = 0;632tail[0] = 0;633pr = 0;634cs = 0;635STBSP__NOTUSED(dp);636goto scopy;637#else638case 'A': // hex float639case 'a': // hex float640h = (f[0] == 'A') ? hexu : hex;641fv = va_arg(va, double);642if (pr == -1)643pr = 6; // default is 6644// read the double into a string645if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))646fl |= STBSP__NEGATIVE;647648s = num + 64;649650stbsp__lead_sign(fl, lead);651652if (dp == -1023)653dp = (n64) ? -1022 : 0;654else655n64 |= (((stbsp__uint64)1) << 52);656n64 <<= (64 - 56);657if (pr < 15)658n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));659// add leading chars660661#ifdef STB_SPRINTF_MSVC_MODE662*s++ = '0';663*s++ = 'x';664#else665lead[1 + lead[0]] = '0';666lead[2 + lead[0]] = 'x';667lead[0] += 2;668#endif669*s++ = h[(n64 >> 60) & 15];670n64 <<= 4;671if (pr)672*s++ = stbsp__period;673sn = s;674675// print the bits676n = pr;677if (n > 13)678n = 13;679if (pr > (stbsp__int32)n)680tz = pr - n;681pr = 0;682while (n--) {683*s++ = h[(n64 >> 60) & 15];684n64 <<= 4;685}686687// print the expo688tail[1] = h[17];689if (dp < 0) {690tail[2] = '-';691dp = -dp;692} else693tail[2] = '+';694n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));695tail[0] = (char)n;696for (;;) {697tail[n] = '0' + dp % 10;698if (n <= 3)699break;700--n;701dp /= 10;702}703704dp = (int)(s - sn);705l = (int)(s - (num + 64));706s = num + 64;707cs = 1 + (3 << 24);708goto scopy;709710case 'G': // float711case 'g': // float712h = (f[0] == 'G') ? hexu : hex;713fv = va_arg(va, double);714if (pr == -1)715pr = 6;716else if (pr == 0)717pr = 1; // default is 6718// read the double into a string719if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))720fl |= STBSP__NEGATIVE;721722// clamp the precision and delete extra zeros after clamp723n = pr;724if (l > (stbsp__uint32)pr)725l = pr;726while ((l > 1) && (pr) && (sn[l - 1] == '0')) {727--pr;728--l;729}730731// should we use %e732if ((dp <= -4) || (dp > (stbsp__int32)n)) {733if (pr > (stbsp__int32)l)734pr = l - 1;735else if (pr)736--pr; // when using %e, there is one digit before the decimal737goto doexpfromg;738}739// this is the insane action to get the pr to match %g semantics for %f740if (dp > 0) {741pr = (dp < (stbsp__int32)l) ? l - dp : 0;742} else {743pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);744}745goto dofloatfromg;746747case 'E': // float748case 'e': // float749h = (f[0] == 'E') ? hexu : hex;750fv = va_arg(va, double);751if (pr == -1)752pr = 6; // default is 6753// read the double into a string754if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))755fl |= STBSP__NEGATIVE;756doexpfromg:757tail[0] = 0;758stbsp__lead_sign(fl, lead);759if (dp == STBSP__SPECIAL) {760s = (char *)sn;761cs = 0;762pr = 0;763goto scopy;764}765s = num + 64;766// handle leading chars767*s++ = sn[0];768769if (pr)770*s++ = stbsp__period;771772// handle after decimal773if ((l - 1) > (stbsp__uint32)pr)774l = pr + 1;775for (n = 1; n < l; n++)776*s++ = sn[n];777// trailing zeros778tz = pr - (l - 1);779pr = 0;780// dump expo781tail[1] = h[0xe];782dp -= 1;783if (dp < 0) {784tail[2] = '-';785dp = -dp;786} else787tail[2] = '+';788#ifdef STB_SPRINTF_MSVC_MODE789n = 5;790#else791n = (dp >= 100) ? 5 : 4;792#endif793tail[0] = (char)n;794for (;;) {795tail[n] = '0' + dp % 10;796if (n <= 3)797break;798--n;799dp /= 10;800}801cs = 1 + (3 << 24); // how many tens802goto flt_lead;803804case 'f': // float805fv = va_arg(va, double);806doafloat:807// do kilos808if (fl & STBSP__METRIC_SUFFIX) {809double divisor;810divisor = 1000.0f;811if (fl & STBSP__METRIC_1024)812divisor = 1024.0;813while (fl < 0x4000000) {814if ((fv < divisor) && (fv > -divisor))815break;816fv /= divisor;817fl += 0x1000000;818}819}820if (pr == -1)821pr = 6; // default is 6822// read the double into a string823if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))824fl |= STBSP__NEGATIVE;825dofloatfromg:826tail[0] = 0;827stbsp__lead_sign(fl, lead);828if (dp == STBSP__SPECIAL) {829s = (char *)sn;830cs = 0;831pr = 0;832goto scopy;833}834s = num + 64;835836// handle the three decimal varieties837if (dp <= 0) {838stbsp__int32 i;839// handle 0.000*000xxxx840*s++ = '0';841if (pr)842*s++ = stbsp__period;843n = -dp;844if ((stbsp__int32)n > pr)845n = pr;846i = n;847while (i) {848if ((((stbsp__uintptr)s) & 3) == 0)849break;850*s++ = '0';851--i;852}853while (i >= 4) {854*(stbsp__uint32 *)s = 0x30303030;855s += 4;856i -= 4;857}858while (i) {859*s++ = '0';860--i;861}862if ((stbsp__int32)(l + n) > pr)863l = pr - n;864i = l;865while (i) {866*s++ = *sn++;867--i;868}869tz = pr - (n + l);870cs = 1 + (3 << 24); // how many tens did we write (for commas below)871} else {872cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;873if ((stbsp__uint32)dp >= l) {874// handle xxxx000*000.0875n = 0;876for (;;) {877if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {878cs = 0;879*s++ = stbsp__comma;880} else {881*s++ = sn[n];882++n;883if (n >= l)884break;885}886}887if (n < (stbsp__uint32)dp) {888n = dp - n;889if ((fl & STBSP__TRIPLET_COMMA) == 0) {890while (n) {891if ((((stbsp__uintptr)s) & 3) == 0)892break;893*s++ = '0';894--n;895}896while (n >= 4) {897*(stbsp__uint32 *)s = 0x30303030;898s += 4;899n -= 4;900}901}902while (n) {903if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {904cs = 0;905*s++ = stbsp__comma;906} else {907*s++ = '0';908--n;909}910}911}912cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens913if (pr) {914*s++ = stbsp__period;915tz = pr;916}917} else {918// handle xxxxx.xxxx000*000919n = 0;920for (;;) {921if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {922cs = 0;923*s++ = stbsp__comma;924} else {925*s++ = sn[n];926++n;927if (n >= (stbsp__uint32)dp)928break;929}930}931cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens932if (pr)933*s++ = stbsp__period;934if ((l - dp) > (stbsp__uint32)pr)935l = pr + dp;936while (n < l) {937*s++ = sn[n];938++n;939}940tz = pr - (l - dp);941}942}943pr = 0;944945// handle k,m,g,t946if (fl & STBSP__METRIC_SUFFIX) {947char idx;948idx = 1;949if (fl & STBSP__METRIC_NOSPACE)950idx = 0;951tail[0] = idx;952tail[1] = ' ';953{954if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.955if (fl & STBSP__METRIC_1024)956tail[idx + 1] = "_KMGT"[fl >> 24];957else958tail[idx + 1] = "_kMGT"[fl >> 24];959idx++;960// If printing kibits and not in jedec, add the 'i'.961if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {962tail[idx + 1] = 'i';963idx++;964}965tail[0] = idx;966}967}968};969970flt_lead:971// get the length that we copied972l = (stbsp__uint32)(s - (num + 64));973s = num + 64;974goto scopy;975#endif976977case 'B': // upper binary978case 'b': // lower binary979h = (f[0] == 'B') ? hexu : hex;980lead[0] = 0;981if (fl & STBSP__LEADING_0X) {982lead[0] = 2;983lead[1] = '0';984lead[2] = h[0xb];985}986l = (8 << 4) | (1 << 8);987goto radixnum;988989case 'o': // octal990h = hexu;991lead[0] = 0;992if (fl & STBSP__LEADING_0X) {993lead[0] = 1;994lead[1] = '0';995}996l = (3 << 4) | (3 << 8);997goto radixnum;998999case 'p': // pointer1000fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;1001pr = sizeof(void *) * 2;1002fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros1003// fall through - to X10041005case 'X': // upper hex1006case 'x': // lower hex1007h = (f[0] == 'X') ? hexu : hex;1008l = (4 << 4) | (4 << 8);1009lead[0] = 0;1010if (fl & STBSP__LEADING_0X) {1011lead[0] = 2;1012lead[1] = '0';1013lead[2] = h[16];1014}1015radixnum:1016// get the number1017if (fl & STBSP__INTMAX)1018n64 = va_arg(va, stbsp__uint64);1019else1020n64 = va_arg(va, stbsp__uint32);10211022s = num + STBSP__NUMSZ;1023dp = 0;1024// clear tail, and clear leading if value is zero1025tail[0] = 0;1026if (n64 == 0) {1027lead[0] = 0;1028if (pr == 0) {1029l = 0;1030cs = 0;1031goto scopy;1032}1033}1034// convert to string1035for (;;) {1036*--s = h[n64 & ((1 << (l >> 8)) - 1)];1037n64 >>= (l >> 8);1038if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))1039break;1040if (fl & STBSP__TRIPLET_COMMA) {1041++l;1042if ((l & 15) == ((l >> 4) & 15)) {1043l &= ~15;1044*--s = stbsp__comma;1045}1046}1047};1048// get the tens and the comma pos1049cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);1050// get the length that we copied1051l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);1052// copy it1053goto scopy;10541055case 'u': // unsigned1056case 'i':1057case 'd': // integer1058// get the integer and abs it1059if (fl & STBSP__INTMAX) {1060stbsp__int64 i64 = va_arg(va, stbsp__int64);1061n64 = (stbsp__uint64)i64;1062if ((f[0] != 'u') && (i64 < 0)) {1063n64 = (stbsp__uint64)-i64;1064fl |= STBSP__NEGATIVE;1065}1066} else {1067stbsp__int32 i = va_arg(va, stbsp__int32);1068n64 = (stbsp__uint32)i;1069if ((f[0] != 'u') && (i < 0)) {1070n64 = (stbsp__uint32)-i;1071fl |= STBSP__NEGATIVE;1072}1073}10741075#ifndef STB_SPRINTF_NOFLOAT1076if (fl & STBSP__METRIC_SUFFIX) {1077if (n64 < 1024)1078pr = 0;1079else if (pr == -1)1080pr = 1;1081fv = (double)(stbsp__int64)n64;1082goto doafloat;1083}1084#endif10851086// convert to string1087s = num + STBSP__NUMSZ;1088l = 0;10891090for (;;) {1091// do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)1092char *o = s - 8;1093if (n64 >= 100000000) {1094n = (stbsp__uint32)(n64 % 100000000);1095n64 /= 100000000;1096} else {1097n = (stbsp__uint32)n64;1098n64 = 0;1099}1100if ((fl & STBSP__TRIPLET_COMMA) == 0) {1101do {1102s -= 2;1103*(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];1104n /= 100;1105} while (n);1106}1107while (n) {1108if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {1109l = 0;1110*--s = stbsp__comma;1111--o;1112} else {1113*--s = (char)(n % 10) + '0';1114n /= 10;1115}1116}1117if (n64 == 0) {1118if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))1119++s;1120break;1121}1122while (s != o)1123if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {1124l = 0;1125*--s = stbsp__comma;1126--o;1127} else {1128*--s = '0';1129}1130}11311132tail[0] = 0;1133stbsp__lead_sign(fl, lead);11341135// get the length that we copied1136l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);1137if (l == 0) {1138*--s = '0';1139l = 1;1140}1141cs = l + (3 << 24);1142if (pr < 0)1143pr = 0;11441145scopy:1146// get fw=leading/trailing space, pr=leading zeros1147if (pr < (stbsp__int32)l)1148pr = l;1149n = pr + lead[0] + tail[0] + tz;1150if (fw < (stbsp__int32)n)1151fw = n;1152fw -= n;1153pr -= l;11541155// handle right justify and leading zeros1156if ((fl & STBSP__LEFTJUST) == 0) {1157if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr1158{1159pr = (fw > pr) ? fw : pr;1160fw = 0;1161} else {1162fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas1163}1164}11651166// copy the spaces and/or zeros1167if (fw + pr) {1168stbsp__int32 i;1169stbsp__uint32 c;11701171// copy leading spaces (or when doing %8.4d stuff)1172if ((fl & STBSP__LEFTJUST) == 0)1173while (fw > 0) {1174stbsp__cb_buf_clamp(i, fw);1175fw -= i;1176while (i) {1177if ((((stbsp__uintptr)bf) & 3) == 0)1178break;1179*bf++ = ' ';1180--i;1181}1182while (i >= 4) {1183*(stbsp__uint32 *)bf = 0x20202020;1184bf += 4;1185i -= 4;1186}1187while (i) {1188*bf++ = ' ';1189--i;1190}1191stbsp__chk_cb_buf(1);1192}11931194// copy leader1195sn = lead + 1;1196while (lead[0]) {1197stbsp__cb_buf_clamp(i, lead[0]);1198lead[0] -= (char)i;1199while (i) {1200*bf++ = *sn++;1201--i;1202}1203stbsp__chk_cb_buf(1);1204}12051206// copy leading zeros1207c = cs >> 24;1208cs &= 0xffffff;1209cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;1210while (pr > 0) {1211stbsp__cb_buf_clamp(i, pr);1212pr -= i;1213if ((fl & STBSP__TRIPLET_COMMA) == 0) {1214while (i) {1215if ((((stbsp__uintptr)bf) & 3) == 0)1216break;1217*bf++ = '0';1218--i;1219}1220while (i >= 4) {1221*(stbsp__uint32 *)bf = 0x30303030;1222bf += 4;1223i -= 4;1224}1225}1226while (i) {1227if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {1228cs = 0;1229*bf++ = stbsp__comma;1230} else1231*bf++ = '0';1232--i;1233}1234stbsp__chk_cb_buf(1);1235}1236}12371238// copy leader if there is still one1239sn = lead + 1;1240while (lead[0]) {1241stbsp__int32 i;1242stbsp__cb_buf_clamp(i, lead[0]);1243lead[0] -= (char)i;1244while (i) {1245*bf++ = *sn++;1246--i;1247}1248stbsp__chk_cb_buf(1);1249}12501251// copy the string1252n = l;1253while (n) {1254stbsp__int32 i;1255stbsp__cb_buf_clamp(i, n);1256n -= i;1257STBSP__UNALIGNED(while (i >= 4) {1258*(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s;1259bf += 4;1260s += 4;1261i -= 4;1262})1263while (i) {1264*bf++ = *s++;1265--i;1266}1267stbsp__chk_cb_buf(1);1268}12691270// copy trailing zeros1271while (tz) {1272stbsp__int32 i;1273stbsp__cb_buf_clamp(i, tz);1274tz -= i;1275while (i) {1276if ((((stbsp__uintptr)bf) & 3) == 0)1277break;1278*bf++ = '0';1279--i;1280}1281while (i >= 4) {1282*(stbsp__uint32 *)bf = 0x30303030;1283bf += 4;1284i -= 4;1285}1286while (i) {1287*bf++ = '0';1288--i;1289}1290stbsp__chk_cb_buf(1);1291}12921293// copy tail if there is one1294sn = tail + 1;1295while (tail[0]) {1296stbsp__int32 i;1297stbsp__cb_buf_clamp(i, tail[0]);1298tail[0] -= (char)i;1299while (i) {1300*bf++ = *sn++;1301--i;1302}1303stbsp__chk_cb_buf(1);1304}13051306// handle the left justify1307if (fl & STBSP__LEFTJUST)1308if (fw > 0) {1309while (fw) {1310stbsp__int32 i;1311stbsp__cb_buf_clamp(i, fw);1312fw -= i;1313while (i) {1314if ((((stbsp__uintptr)bf) & 3) == 0)1315break;1316*bf++ = ' ';1317--i;1318}1319while (i >= 4) {1320*(stbsp__uint32 *)bf = 0x20202020;1321bf += 4;1322i -= 4;1323}1324while (i--)1325*bf++ = ' ';1326stbsp__chk_cb_buf(1);1327}1328}1329break;13301331default: // unknown, just copy code1332s = num + STBSP__NUMSZ - 1;1333*s = f[0];1334l = 1;1335fw = fl = 0;1336lead[0] = 0;1337tail[0] = 0;1338pr = 0;1339dp = 0;1340cs = 0;1341goto scopy;1342}1343++f;1344}1345endfmt:13461347if (!callback)1348*bf = 0;1349else1350stbsp__flush_cb();13511352done:1353return tlen + (int)(bf - buf);1354}13551356// cleanup1357#undef STBSP__LEFTJUST1358#undef STBSP__LEADINGPLUS1359#undef STBSP__LEADINGSPACE1360#undef STBSP__LEADING_0X1361#undef STBSP__LEADINGZERO1362#undef STBSP__INTMAX1363#undef STBSP__TRIPLET_COMMA1364#undef STBSP__NEGATIVE1365#undef STBSP__METRIC_SUFFIX1366#undef STBSP__NUMSZ1367#undef stbsp__chk_cb_bufL1368#undef stbsp__chk_cb_buf1369#undef stbsp__flush_cb1370#undef stbsp__cb_buf_clamp13711372// ============================================================================1373// wrapper functions13741375STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)1376{1377int result;1378va_list va;1379va_start(va, fmt);1380result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);1381va_end(va);1382return result;1383}13841385typedef struct stbsp__context {1386char *buf;1387int count;1388int length;1389char tmp[STB_SPRINTF_MIN];1390} stbsp__context;13911392static char *stbsp__clamp_callback(const char *buf, void *user, int len)1393{1394stbsp__context *c = (stbsp__context *)user;1395c->length += len;13961397if (len > c->count)1398len = c->count;13991400if (len) {1401if (buf != c->buf) {1402const char *s, *se;1403char *d;1404d = c->buf;1405s = buf;1406se = buf + len;1407do {1408*d++ = *s++;1409} while (s < se);1410}1411c->buf += len;1412c->count -= len;1413}14141415if (c->count <= 0)1416return c->tmp;1417return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can1418}14191420static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )1421{1422stbsp__context * c = (stbsp__context*)user;1423(void) sizeof(buf);14241425c->length += len;1426return c->tmp; // go direct into buffer if you can1427}14281429STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )1430{1431stbsp__context c;14321433if ( (count == 0) && !buf )1434{1435c.length = 0;14361437STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );1438}1439else1440{1441int l;14421443c.buf = buf;1444c.count = count;1445c.length = 0;14461447STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );14481449// zero-terminate1450l = (int)( c.buf - buf );1451if ( l >= count ) // should never be greater, only equal (or less) than count1452l = count - 1;1453buf[l] = 0;1454}14551456return c.length;1457}14581459STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)1460{1461int result;1462va_list va;1463va_start(va, fmt);14641465result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);1466va_end(va);14671468return result;1469}14701471STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)1472{1473return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);1474}14751476// =======================================================================1477// low level float utility functions14781479#ifndef STB_SPRINTF_NOFLOAT14801481// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)1482#define STBSP__COPYFP(dest, src) \1483{ \1484int cn; \1485for (cn = 0; cn < 8; cn++) \1486((char *)&dest)[cn] = ((char *)&src)[cn]; \1487}14881489// get float info1490static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)1491{1492double d;1493stbsp__int64 b = 0;14941495// load value and round at the frac_digits1496d = value;14971498STBSP__COPYFP(b, d);14991500*bits = b & ((((stbsp__uint64)1) << 52) - 1);1501*expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);15021503return (stbsp__int32)((stbsp__uint64) b >> 63);1504}15051506static double const stbsp__bot[23] = {15071e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,15081e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+0221509};1510static double const stbsp__negbot[22] = {15111e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,15121e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-0221513};1514static double const stbsp__negboterr[22] = {1515-5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,15164.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029,1517-3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035,15182.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-0391519};1520static double const stbsp__top[13] = {15211e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+2991522};1523static double const stbsp__negtop[13] = {15241e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-2991525};1526static double const stbsp__toperr[13] = {15278388608,15286.8601809640529717e+028,1529-7.253143638152921e+052,1530-4.3377296974619174e+075,1531-1.5559416129466825e+098,1532-3.2841562489204913e+121,1533-3.7745893248228135e+144,1534-1.7356668416969134e+167,1535-3.8893577551088374e+190,1536-9.9566444326005119e+213,15376.3641293062232429e+236,1538-5.2069140800249813e+259,1539-5.2504760255204387e+2821540};1541static double const stbsp__negtoperr[13] = {15423.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109,1543-5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201,15447.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,15458.0970921678014997e-3171546};15471548#if defined(_MSC_VER) && (_MSC_VER <= 1200)1549static stbsp__uint64 const stbsp__powten[20] = {15501,155110,1552100,15531000,155410000,1555100000,15561000000,155710000000,1558100000000,15591000000000,156010000000000,1561100000000000,15621000000000000,156310000000000000,1564100000000000000,15651000000000000000,156610000000000000000,1567100000000000000000,15681000000000000000000,156910000000000000000000U1570};1571#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)1572#else1573static stbsp__uint64 const stbsp__powten[20] = {15741,157510,1576100,15771000,157810000,1579100000,15801000000,158110000000,1582100000000,15831000000000,158410000000000ULL,1585100000000000ULL,15861000000000000ULL,158710000000000000ULL,1588100000000000000ULL,15891000000000000000ULL,159010000000000000000ULL,1591100000000000000000ULL,15921000000000000000000ULL,159310000000000000000000ULL1594};1595#define stbsp__tento19th (1000000000000000000ULL)1596#endif15971598#define stbsp__ddmulthi(oh, ol, xh, yh) \1599{ \1600double ahi = 0, alo, bhi = 0, blo; \1601stbsp__int64 bt; \1602oh = xh * yh; \1603STBSP__COPYFP(bt, xh); \1604bt &= ((~(stbsp__uint64)0) << 27); \1605STBSP__COPYFP(ahi, bt); \1606alo = xh - ahi; \1607STBSP__COPYFP(bt, yh); \1608bt &= ((~(stbsp__uint64)0) << 27); \1609STBSP__COPYFP(bhi, bt); \1610blo = yh - bhi; \1611ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \1612}16131614#define stbsp__ddtoS64(ob, xh, xl) \1615{ \1616double ahi = 0, alo, vh, t; \1617ob = (stbsp__int64)xh; \1618vh = (double)ob; \1619ahi = (xh - vh); \1620t = (ahi - xh); \1621alo = (xh - (ahi - t)) - (vh + t); \1622ob += (stbsp__int64)(ahi + alo + xl); \1623}16241625#define stbsp__ddrenorm(oh, ol) \1626{ \1627double s; \1628s = oh + ol; \1629ol = ol - (s - oh); \1630oh = s; \1631}16321633#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);16341635#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);16361637static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +3501638{1639double ph, pl;1640if ((power >= 0) && (power <= 22)) {1641stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);1642} else {1643stbsp__int32 e, et, eb;1644double p2h, p2l;16451646e = power;1647if (power < 0)1648e = -e;1649et = (e * 0x2c9) >> 14; /* %23 */1650if (et > 13)1651et = 13;1652eb = e - (et * 23);16531654ph = d;1655pl = 0.0;1656if (power < 0) {1657if (eb) {1658--eb;1659stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);1660stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);1661}1662if (et) {1663stbsp__ddrenorm(ph, pl);1664--et;1665stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);1666stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);1667ph = p2h;1668pl = p2l;1669}1670} else {1671if (eb) {1672e = eb;1673if (eb > 22)1674eb = 22;1675e -= eb;1676stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);1677if (e) {1678stbsp__ddrenorm(ph, pl);1679stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);1680stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);1681ph = p2h;1682pl = p2l;1683}1684}1685if (et) {1686stbsp__ddrenorm(ph, pl);1687--et;1688stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);1689stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);1690ph = p2h;1691pl = p2l;1692}1693}1694}1695stbsp__ddrenorm(ph, pl);1696*ohi = ph;1697*olo = pl;1698}16991700// given a float value, returns the significant bits in bits, and the position of the1701// decimal point in decimal_pos. +/-INF and NAN are specified by special values1702// returned in the decimal_pos parameter.1703// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x800000001704static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)1705{1706double d;1707stbsp__int64 bits = 0;1708stbsp__int32 expo, e, ng, tens;17091710d = value;1711STBSP__COPYFP(bits, d);1712expo = (stbsp__int32)((bits >> 52) & 2047);1713ng = (stbsp__int32)((stbsp__uint64) bits >> 63);1714if (ng)1715d = -d;17161717if (expo == 2047) // is nan or inf?1718{1719*start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";1720*decimal_pos = STBSP__SPECIAL;1721*len = 3;1722return ng;1723}17241725if (expo == 0) // is zero or denormal1726{1727if (((stbsp__uint64) bits << 1) == 0) // do zero1728{1729*decimal_pos = 1;1730*start = out;1731out[0] = '0';1732*len = 1;1733return ng;1734}1735// find the right expo for denormals1736{1737stbsp__int64 v = ((stbsp__uint64)1) << 51;1738while ((bits & v) == 0) {1739--expo;1740v >>= 1;1741}1742}1743}17441745// find the decimal exponent as well as the decimal bits of the value1746{1747double ph, pl;17481749// log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..20461750tens = expo - 1023;1751tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);17521753// move the significant bits into position and stick them into an int1754stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);17551756// get full as much precision from double-double as possible1757stbsp__ddtoS64(bits, ph, pl);17581759// check if we undershot1760if (((stbsp__uint64)bits) >= stbsp__tento19th)1761++tens;1762}17631764// now do the rounding in integer land1765frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);1766if ((frac_digits < 24)) {1767stbsp__uint32 dg = 1;1768if ((stbsp__uint64)bits >= stbsp__powten[9])1769dg = 10;1770while ((stbsp__uint64)bits >= stbsp__powten[dg]) {1771++dg;1772if (dg == 20)1773goto noround;1774}1775if (frac_digits < dg) {1776stbsp__uint64 r;1777// add 0.5 at the right position and round1778e = dg - frac_digits;1779if ((stbsp__uint32)e >= 24)1780goto noround;1781r = stbsp__powten[e];1782bits = bits + (r / 2);1783if ((stbsp__uint64)bits >= stbsp__powten[dg])1784++tens;1785bits /= r;1786}1787noround:;1788}17891790// kill long trailing runs of zeros1791if (bits) {1792stbsp__uint32 n;1793for (;;) {1794if (bits <= 0xffffffff)1795break;1796if (bits % 1000)1797goto donez;1798bits /= 1000;1799}1800n = (stbsp__uint32)bits;1801while ((n % 1000) == 0)1802n /= 1000;1803bits = n;1804donez:;1805}18061807// convert to string1808out += 64;1809e = 0;1810for (;;) {1811stbsp__uint32 n;1812char *o = out - 8;1813// do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)1814if (bits >= 100000000) {1815n = (stbsp__uint32)(bits % 100000000);1816bits /= 100000000;1817} else {1818n = (stbsp__uint32)bits;1819bits = 0;1820}1821while (n) {1822out -= 2;1823*(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];1824n /= 100;1825e += 2;1826}1827if (bits == 0) {1828if ((e) && (out[0] == '0')) {1829++out;1830--e;1831}1832break;1833}1834while (out != o) {1835*--out = '0';1836++e;1837}1838}18391840*decimal_pos = tens;1841*start = out;1842*len = e;1843return ng;1844}18451846#undef stbsp__ddmulthi1847#undef stbsp__ddrenorm1848#undef stbsp__ddmultlo1849#undef stbsp__ddmultlos1850#undef STBSP__SPECIAL1851#undef STBSP__COPYFP18521853#endif // STB_SPRINTF_NOFLOAT18541855// clean up1856#undef stbsp__uint161857#undef stbsp__uint321858#undef stbsp__int321859#undef stbsp__uint641860#undef stbsp__int641861#undef STBSP__UNALIGNED18621863#endif // STB_SPRINTF_IMPLEMENTATION18641865/*1866------------------------------------------------------------------------------1867This software is available under 2 licenses -- choose whichever you prefer.1868------------------------------------------------------------------------------1869ALTERNATIVE A - MIT License1870Copyright (c) 2017 Sean Barrett1871Permission is hereby granted, free of charge, to any person obtaining a copy of1872this software and associated documentation files (the "Software"), to deal in1873the Software without restriction, including without limitation the rights to1874use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies1875of the Software, and to permit persons to whom the Software is furnished to do1876so, subject to the following conditions:1877The above copyright notice and this permission notice shall be included in all1878copies or substantial portions of the Software.1879THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1880IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1881FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE1882AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER1883LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,1884OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE1885SOFTWARE.1886------------------------------------------------------------------------------1887ALTERNATIVE B - Public Domain (www.unlicense.org)1888This is free and unencumbered software released into the public domain.1889Anyone is free to copy, modify, publish, use, compile, sell, or distribute this1890software, either in source code form or as a compiled binary, for any purpose,1891commercial or non-commercial, and by any means.1892In jurisdictions that recognize copyright laws, the author or authors of this1893software dedicate any and all copyright interest in the software to the public1894domain. We make this dedication for the benefit of the public at large and to1895the detriment of our heirs and successors. We intend this dedication to be an1896overt act of relinquishment in perpetuity of all present and future rights to1897this software under copyright law.1898THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1899IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1900FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE1901AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN1902ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION1903WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.1904------------------------------------------------------------------------------1905*/190619071908