Created
August 16, 2016 15:57
-
-
Save jkbonfield/32702b1b2aa4de7b7e483d6c1156c51a to your computer and use it in GitHub Desktop.
printf %g equivalent with explicit force to floating point type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <math.h> | |
| typedef struct __kstring_t { | |
| size_t l, m; | |
| char *s; | |
| } kstring_t; | |
| #ifndef kroundup32 | |
| #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) | |
| #endif | |
| #ifndef kroundup_size_t | |
| #define kroundup_size_t(x) (--(x), \ | |
| (x)|=(x)>>(sizeof(size_t)/8), /* 0 or 1 */ \ | |
| (x)|=(x)>>(sizeof(size_t)/4), /* 1 or 2 */ \ | |
| (x)|=(x)>>(sizeof(size_t)/2), /* 2 or 4 */ \ | |
| (x)|=(x)>>(sizeof(size_t)), /* 4 or 8 */ \ | |
| (x)|=(x)>>(sizeof(size_t)*2), /* 8 or 16 */ \ | |
| (x)|=(x)>>(sizeof(size_t)*4), /* 16 or 32 */ \ | |
| ++(x)) | |
| #endif | |
| static inline int ks_resize(kstring_t *s, size_t size) | |
| { | |
| if (s->m < size) { | |
| char *tmp; | |
| kroundup_size_t(size); | |
| tmp = (char*)realloc(s->s, size); | |
| if (!tmp) | |
| return -1; | |
| s->s = tmp; | |
| s->m = size; | |
| } | |
| return 0; | |
| } | |
| static inline int kputsn(const char *p, int l, kstring_t *s) | |
| { | |
| if (ks_resize(s, s->l + l + 2) < 0) | |
| return EOF; | |
| memcpy(s->s + s->l, p, l); | |
| s->l += l; | |
| s->s[s->l] = 0; | |
| return l; | |
| } | |
| static inline int kputc(int c, kstring_t *s) | |
| { | |
| if (ks_resize(s, s->l + 2) < 0) | |
| return EOF; | |
| s->s[s->l++] = c; | |
| s->s[s->l] = 0; | |
| return c; | |
| } | |
| int ksprintf_g(kstring_t *s, double d) { | |
| int len = 0; | |
| char buf[21], *cp = buf+20, *ep; | |
| if (d == 0) { | |
| if (signbit(d)) { | |
| kputsn("-0.0",4,s); | |
| return 2; | |
| } else { | |
| kputsn("0.0",3,s); | |
| return 1; | |
| } | |
| } | |
| if (d < 0) { | |
| kputc('-',s); | |
| d=-d; | |
| } | |
| if (d < 0.0001 || d > 999999) { | |
| if (ks_resize(s, s->l + 50) < 0) | |
| return EOF; | |
| // We let stdio handle the exponent cases | |
| len = sprintf(s->s + s->l, "%g", d); | |
| s->l += len; | |
| return len; | |
| } | |
| uint64_t i = d*10000000000; | |
| // Correction for rounding - rather ugly | |
| // Optimised for small numbers. | |
| // Better still would be __builtin_clz on hi/lo 32 and get the | |
| // starting point very rapidly. | |
| if (d<.0001) | |
| i+=0; | |
| else if (d<0.001) | |
| i+=5; | |
| else if (d < 0.01) | |
| i+=50; | |
| else if (d < 0.1) | |
| i+=500; | |
| else if (d < 1) | |
| i+=5000; | |
| else if (d < 10) | |
| i+=50000; | |
| else if (d < 100) | |
| i+=500000; | |
| else if (d < 1000) | |
| i+=5000000; | |
| else if (d < 10000) | |
| i+=50000000; | |
| else if (d < 100000) | |
| i+=500000000; | |
| else | |
| i+=5000000000; | |
| do { | |
| *--cp = '0' + i%10; | |
| i /= 10; | |
| } while (i >= 1); | |
| buf[20] = 0; | |
| int p = buf+20-cp; | |
| if (p <= 10) { // d < 1 | |
| //assert(d/1); | |
| cp[6] = 0; ep = cp+5;// 6 precision | |
| while (p < 10) { | |
| *--cp = '0'; | |
| p++; | |
| } | |
| *--cp = '.'; | |
| *--cp = '0'; | |
| } else { | |
| char *xp = --cp; | |
| while (p > 10) { | |
| xp[0] = xp[1]; | |
| p--; | |
| xp++; | |
| } | |
| xp[0] = '.'; | |
| cp[7] = 0; ep=cp+6; | |
| if (cp[6] == '.') cp[6] = 0; | |
| } | |
| // Cull trailing zeros | |
| while (*ep == '0' && ep > cp) | |
| ep--; | |
| char *z = ep+1; | |
| while (ep > cp) { | |
| if (*ep == '.') { | |
| if (z[-1] == '.') { | |
| // small whole number, add explicit "." | |
| z[0] = 0; | |
| } else { | |
| z[0] = 0; | |
| } | |
| break; | |
| } | |
| ep--; | |
| } | |
| if (ep == cp) { | |
| // 6 digit whole number or truncated to 6 digits. | |
| // Misleading as "123456.7" becomes "123457." implying | |
| // 123457.something and >= 123457. However it forces | |
| // the number to be interpreted as floating point by buggy | |
| // software. | |
| cp[6] = '.'; | |
| cp[7] = 0; | |
| } | |
| len = strlen(cp); | |
| kputsn(cp, len, s); | |
| return len; | |
| } | |
| int main(int argc, char **argv) { | |
| char buf1[100], buf2[100]; | |
| kstring_t s = {0, 100, buf1}; | |
| double d = atof(argv[1]); | |
| ksprintf_g(&s, d); | |
| sprintf(buf2, "%g", d); | |
| printf("libc ='%s'\n" | |
| "kstring='%s'\n", | |
| buf2, buf1); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment