+ * Copyright (c) 2021 Takehiko NOZAKI
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#if defined(HAVE_MATH_H)
+extern char *__hdtoa(double, const char *, int, int *, int *, char **);
+extern void __freedtoa(char *);
+#define freedtoa __freedtoa
+#define arraycount(array) __arraycount(array)
+#define arraycount(array) (sizeof(array)/sizeof(array[0]))
+#define DTOATEST(value) { .dvalue = value, .svalue = #value }
+static const char zeros[PADSIZ] = "00000000000000000000";
+static const char spaces[PADSIZ] = " ";
+pad(const char *filler, int len, FILE *fp)
+ if (fwrite(filler, 1, PADSIZ, fp) != PADSIZ)
+ if (fwrite(filler, 1, len, fp) != len)
+lpad(int sign, int width, int siz, int flags, FILE *fp)
+ if (width > siz && (flags & (MINUS|ZERO)) == 0 &&
+ pad(spaces, width - siz, fp))
+ if (sign && putc(sign, fp) == EOF)
+ if ((flags & HEX) && fwrite("0x", 1, 2, fp) != 2)
+ if (width > siz && (flags & (MINUS|ZERO)) == ZERO &&
+ pad(zeros, width - siz, fp))
+rpad(int width, int siz, int flags, FILE *fp)
+ if (width > siz && (flags & MINUS) &&
+ pad(spaces, width - siz, fp))
+ else if (flags & SPACE)
+cvt_inf(int width, int flags, FILE *fp)
+ sign = cvt_sign(flags);
+ if (lpad(sign, width, siz, flags & ~ZERO, fp))
+ if (fwrite("inf", 1, 3, fp) != 3)
+ if (rpad(width, siz, flags, fp))
+cvt_nan(int width, int flags, FILE *fp)
+ if (lpad('\0', width, 3, flags & ~ZERO, fp))
+ if (fwrite("nan", 1, 3, fp) != 3)
+ if (rpad(width, 3, flags, fp))
+cvt_exp(int exp, FILE *fp)
+ char buf[MAXEXPSIZ + 2], *expstr;
+ expstr = &buf[sizeof(buf)];
+ *--expstr = to_char(exp % 10);
+ *--expstr = to_char(exp);
+ len = &buf[sizeof(buf)] - expstr;
+ if (fwrite(expstr, 1, len, fp) != len)
+cvt_asize(int sign, int len, int prec, int exp, int flags)
+ d = (f > 0 || flags & SHARP) ? 1 : 0;
+ return x + s + i + f + d + p;
+cvt_afmt(char *head, int len, int width, int prec, int exp, int flags, FILE *fp)
+ sign = cvt_sign(flags);
+ size = cvt_asize(sign, len, prec, exp, flags);
+ if (lpad(sign, width, size, flags|HEX, fp))
+ if (putc(*head, fp) == EOF)
+ if (((flags & SHARP) || prec > 0) && putc('.', fp) == EOF)
+ if (--len > 0 && fwrite(&head[1], 1, len, fp) != len)
+ if (prec > len && pad(zeros, prec - len, fp))
+ if (rpad(width, size, flags, fp))
+acvt(double dvalue, int width, int prec, FILE *fp)
+ static const char *xdigit = "0123456789abcdef";
+ int flags, exp, neg, len, ret;
+ head = hdtoa(dvalue, xdigit, prec, &exp, &neg, &tail);
+ ret = cvt_inf(width, flags, fp);
+ ret = cvt_nan(width, flags, fp);
+ ret = cvt_afmt(head, len, width, prec - 1, exp - 1, flags, fp);
+main(int argc, char *argv[])
+ static const struct dtoatestcase t[] = {
+#if defined(HAVE_MATH_H) && !defined(__vax__)
+ DTOATEST(1123456789.0),
+ DTOATEST(11234567890123.0),
+ for (i = 0; i < arraycount(t); ++i) {
+ for (prec = -1; prec <= 20; ++prec) {
+ if (asprintf(&expect, "%30.*a", prec, t[i].dvalue) < 0)
+ fp = open_memstream(&result, &n);
+ if (acvt(t[i].dvalue, 30, prec, fp))
+ printf("testcase:[%s], expect:[%s], result[%s]\n",
+ t[i].svalue, expect, result);
+ if (strcmp(result, expect))