Snippets

Takehiko NOZAKI gdtoa usage (printf %e conversion like)

Updated by Takehiko NOZAKI

File ecvt.c Modified

  • Ignore whitespace
  • Hide word diff
 int
 ecvt(double dvalue, int width, int prec, FILE *fp)
 {
-	int flags, exp, neg, ret;
+	int flags, exp, neg, len, ret;
 	char *head, *tail;
 
 	flags = 0;
 	head = dtoa(dvalue, 2, prec + 1, &exp, &neg, &tail);
 	if (head == NULL)
 		return 1;
+	len = tail - head;
 	if (neg)
 		flags |= NEGATIVE;
 	if (exp == 9999) {
 			ret = 1;
 		}
 	} else {
-		ret = cvt_efmt(head, tail - head, width, prec, exp - 1, flags, fp);
+		ret = cvt_efmt(head, len, width, prec, exp - 1, flags, fp);
 	}
 	freedtoa(head);
 	return ret;
Updated by Takehiko NOZAKI

File ecvt.c Modified

  • Ignore whitespace
  • Hide word diff
 #define SPACE		0x10
 #define SHARP		0x20
 #define ZERO		0x40
+#define HEX			0x80
 
 #define PADSIZ	20
 static const char zeros[PADSIZ] = "00000000000000000000";
 		return 1;
 	if (sign && putc(sign, fp) == EOF)
 		return 1;
+	if ((flags & HEX) && fwrite("0x", 1, 2, fp) != 2)
+		return 1;
 	if (width > siz && (flags & (MINUS|ZERO)) == ZERO &&
 	    pad(zeros, width - siz, fp))
 		return 1;
Updated by Takehiko NOZAKI

File ecvt.c Modified

  • Ignore whitespace
  • Hide word diff
 #define MAXEXPSIZ	4
 
 #define NEGATIVE	0x1
-#define ALT		0x2
-#define SIGN		0x4
-#define SPACE		0x8
+#define SQUOTE		0x2
+#define MINUS		0x4
+#define PLUS		0x8
+#define SPACE		0x10
+#define SHARP		0x20
+#define ZERO		0x40
+
+#define PADSIZ	20
+static const char zeros[PADSIZ] = "00000000000000000000";
+static const char spaces[PADSIZ] = "                    ";
 
 static inline int
-cvt_sign(int flags, FILE *fp)
+pad(const char *filler, int len, FILE *fp)
 {
-	if (flags & NEGATIVE) {
-		if (putc('-', fp) == EOF)
-			return 1;
-	} else if (flags & SIGN) {
-		if (putc('+', fp) == EOF)
-			return 1;
-	} else if (flags & SPACE) {
-		if (putc(' ', fp) == EOF)
+	while (len > PADSIZ) {
+		if (fwrite(filler, 1, PADSIZ, fp) != PADSIZ)
 			return 1;
+		len -= PADSIZ;
 	}
+	if (fwrite(filler, 1, len, fp) != len)
+		return 1;
 	return 0;
 }
 
 static inline int
-cvt_inf(int flags, FILE *fp)
+lpad(int sign, int width, int siz, int flags, FILE *fp)
 {
-	if (cvt_sign(flags, fp))
+	if (width > siz && (flags & (MINUS|ZERO)) == 0 &&
+	    pad(spaces, width - siz, fp))
 		return 1;
-	if (fwrite("inf", 1, 3, fp) != 3)
+	if (sign && putc(sign, fp) == EOF)
+		return 1;
+	if (width > siz && (flags & (MINUS|ZERO)) == ZERO &&
+	    pad(zeros, width - siz, fp))
 		return 1;
 	return 0;
 }
 
 static inline int
-cvt_nan(int flags, FILE *fp)
+rpad(int width, int siz, int flags, FILE *fp)
 {
-	if (fwrite("nan", 1, 3, fp) != 3)
+	if (width > siz && (flags & MINUS) &&
+	    pad(spaces, width - siz, fp))
 		return 1;
 	return 0;
 }
 
 static inline int
-fill_zeros(int len, FILE *fp)
+cvt_sign(int flags)
 {
-	static const char zeros[10] = "0000000000";
+	if (flags & NEGATIVE)
+		return '-';
+	else if (flags & PLUS)
+		return '+';
+	else if (flags & SPACE)
+		return ' ';
+	return '\0';
+}
 
-	while (len > (int)sizeof(zeros)) {
-		if (fwrite(zeros, 1, sizeof(zeros), fp) != sizeof(zeros))
-			return 1;
-		len -= sizeof(zeros);
-	}
-	if (fwrite(zeros, 1, len, fp) != len)
+static inline int
+cvt_inf(int width, int flags, FILE *fp)
+{
+	int sign, siz;
+
+	sign = cvt_sign(flags);
+	siz = (sign) ? 4 : 3;
+	if (lpad(sign, width, siz, flags & ~ZERO, fp))
+		return 1;
+	if (fwrite("inf", 1, 3, fp) != 3)
+		return 1;
+	if (rpad(width, siz, flags, fp))
+		return 1;
+	return 0;
+}
+
+static inline int
+cvt_nan(int width, int flags, FILE *fp)
+{
+	if (lpad('\0', width, 3, flags & ~ZERO, fp))
+		return 1;
+	if (fwrite("nan", 1, 3, fp) != 3)
+		return 1;
+	if (rpad(width, 3, flags, fp))
 		return 1;
 	return 0;
 }
 }
 
 static inline int
-cvt_efmt(char *head, int len, int prec, int exp, int flags, FILE *fp)
+cvt_esize(int sign, int len, int prec, int exp, int flags)
 {
-	if (cvt_sign(flags, fp))
+	int s, i, f, d, e;
+
+	s = (sign) ? 1 : 0;
+	i = 1;
+	f = len - 1;
+	if (f < prec)
+		f = prec;
+	d = (f > 0 || flags & SHARP) ? 1 : 0;
+	e = 2;
+	if (exp > 9) {
+		do {
+			++e;
+			exp /= 9;
+		} while (exp > 9);
+		++e;
+	} else {
+		e += 2;
+	}
+	return s + i + f + d + e;
+}
+
+static inline int
+cvt_efmt(char *head, int len, int width, int prec, int exp, int flags, FILE *fp)
+{
+	int sign, size;
+
+	sign = cvt_sign(flags);
+	size = cvt_esize(sign, len, prec, exp, flags);
+	if (lpad(sign, width, size, flags, fp))
 		return 1;
 	if (putc(*head, fp) == EOF)
 		return 1;
-	if (((flags & ALT) || prec > 0) && putc('.', fp) == EOF)
+	if (((flags & SHARP) || prec > 0) && putc('.', fp) == EOF)
 		return 1;
 	if (--len > 0 && fwrite(&head[1], 1, len, fp) != len)
 		return 1;
-	if (prec > len && fill_zeros(prec - len, fp))
+	if (prec > len && pad(zeros, prec - len, fp))
 		return 1;
 	if (cvt_exp(exp, fp))
 		return 1;
+	if (rpad(width, size, flags, fp))
+		return 1;
 	return 0;
 }
 
 int
-ecvt(double dvalue, int prec, FILE *fp)
+ecvt(double dvalue, int width, int prec, FILE *fp)
 {
 	int flags, exp, neg, ret;
 	char *head, *tail;
 
+	flags = 0;
 	if (prec < 0)
 		prec = DEFAULTPREC;
-	flags = 0;
 	head = dtoa(dvalue, 2, prec + 1, &exp, &neg, &tail);
 	if (head == NULL)
 		return 1;
 	if (exp == 9999) {
 		switch (*head) {
 		case 'I':
-			ret = cvt_inf(flags, fp);
+			ret = cvt_inf(width, flags, fp);
 			break;
 		case 'N':
-			ret = cvt_nan(flags, fp);
+			ret = cvt_nan(width, flags, fp);
 			break;
 		default:
 			ret = 1;
 		}
 	} else {
-		ret = cvt_efmt(head, tail - head, prec, exp - 1, flags, fp);
+		ret = cvt_efmt(head, tail - head, width, prec, exp - 1, flags, fp);
 	}
 	freedtoa(head);
 	return ret;
 	for (i = 0; i < arraycount(t); ++i) {
 		for (prec = -1; prec <= 20; ++prec) {
 			expect = NULL;
-			if (asprintf(&expect, "%.*e", prec, t[i].dvalue) < 0)
+			if (asprintf(&expect, "%30.*e", prec, t[i].dvalue) < 0)
 				abort();
 			result = NULL;
 			fp = open_memstream(&result, &n);
 			if (fp == NULL)
 				abort();
-			if (ecvt(t[i].dvalue, prec, fp))
+			if (ecvt(t[i].dvalue, 30, prec, fp))
 				abort();
 			fclose(fp);
 			printf("testcase:[%s], expect:[%s], result[%s]\n",
 			free(result);
 		}
 	}
+	exit(EXIT_SUCCESS);
 }
Updated by Takehiko NOZAKI

File ecvt.c Modified

  • Ignore whitespace
  • Hide word diff
 			if (ecvt(t[i].dvalue, prec, fp))
 				abort();
 			fclose(fp);
-			printf("expect:[%s], result[%s]\n", expect, result);
+			printf("testcase:[%s], expect:[%s], result[%s]\n",
+				t[i].svalue, expect, result);
 			if (strcmp(result, expect))
 				abort();
 			free(expect);
Created by Takehiko NOZAKI

File ecvt.c Added

  • Ignore whitespace
  • Hide word diff
+/*-
+ * Copyright (c) 2021 Takehiko NOZAKI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+
+#if defined(__NetBSD__)
+extern char *__dtoa(double, int, int, int *, int *, char **);
+extern void __freedtoa(char *);
+#define dtoa		__dtoa
+#define freedtoa	__freedtoa
+#include <sys/cdefs.h>
+#define arraycount(array)	__arraycount(array)
+#else
+#include "gdtoa.h"
+#define arraycount(array)       (sizeof(array)/sizeof(array[0]))
+#endif
+
+struct dtoatestcase {
+	double dvalue;
+	const char *svalue;
+};
+
+#define DTOATEST(value) { .dvalue = value, .svalue = #value }
+#define debug	printf
+
+#define DEFAULTPREC	6
+#define MAXEXPSIZ	4
+
+#define NEGATIVE	0x1
+#define ALT		0x2
+#define SIGN		0x4
+#define SPACE		0x8
+
+static inline int
+cvt_sign(int flags, FILE *fp)
+{
+	if (flags & NEGATIVE) {
+		if (putc('-', fp) == EOF)
+			return 1;
+	} else if (flags & SIGN) {
+		if (putc('+', fp) == EOF)
+			return 1;
+	} else if (flags & SPACE) {
+		if (putc(' ', fp) == EOF)
+			return 1;
+	}
+	return 0;
+}
+
+static inline int
+cvt_inf(int flags, FILE *fp)
+{
+	if (cvt_sign(flags, fp))
+		return 1;
+	if (fwrite("inf", 1, 3, fp) != 3)
+		return 1;
+	return 0;
+}
+
+static inline int
+cvt_nan(int flags, FILE *fp)
+{
+	if (fwrite("nan", 1, 3, fp) != 3)
+		return 1;
+	return 0;
+}
+
+static inline int
+fill_zeros(int len, FILE *fp)
+{
+	static const char zeros[10] = "0000000000";
+
+	while (len > (int)sizeof(zeros)) {
+		if (fwrite(zeros, 1, sizeof(zeros), fp) != sizeof(zeros))
+			return 1;
+		len -= sizeof(zeros);
+	}
+	if (fwrite(zeros, 1, len, fp) != len)
+		return 1;
+	return 0;
+}
+
+static inline int
+to_char(int digit)
+{
+	return '0' + digit;
+}
+
+static inline int
+cvt_exp(int exp, FILE *fp)
+{
+	char buf[MAXEXPSIZ + 2], *expstr;
+	int sign;
+	size_t len;
+
+	if (exp >= 0) {
+		sign = '+';
+	} else {
+		sign = '-';
+		exp = -exp;
+	}
+	expstr = &buf[sizeof(buf)];
+	if (exp > 9) {
+		do {
+			*--expstr = to_char(exp % 10);
+			exp /= 10;
+		} while (exp > 9);
+		*--expstr = to_char(exp);
+	} else {
+		*--expstr = to_char(exp);
+		*--expstr = to_char(0);
+	}
+	*--expstr = sign;
+	*--expstr = 'e';
+	len = &buf[sizeof(buf)] - expstr;
+	if (fwrite(expstr, 1, len, fp) != len)
+		return 1;
+	return 0;
+}
+
+static inline int
+cvt_efmt(char *head, int len, int prec, int exp, int flags, FILE *fp)
+{
+	if (cvt_sign(flags, fp))
+		return 1;
+	if (putc(*head, fp) == EOF)
+		return 1;
+	if (((flags & ALT) || prec > 0) && putc('.', fp) == EOF)
+		return 1;
+	if (--len > 0 && fwrite(&head[1], 1, len, fp) != len)
+		return 1;
+	if (prec > len && fill_zeros(prec - len, fp))
+		return 1;
+	if (cvt_exp(exp, fp))
+		return 1;
+	return 0;
+}
+
+int
+ecvt(double dvalue, int prec, FILE *fp)
+{
+	int flags, exp, neg, ret;
+	char *head, *tail;
+
+	if (prec < 0)
+		prec = DEFAULTPREC;
+	flags = 0;
+	head = dtoa(dvalue, 2, prec + 1, &exp, &neg, &tail);
+	if (head == NULL)
+		return 1;
+	if (neg)
+		flags |= NEGATIVE;
+	if (exp == 9999) {
+		switch (*head) {
+		case 'I':
+			ret = cvt_inf(flags, fp);
+			break;
+		case 'N':
+			ret = cvt_nan(flags, fp);
+			break;
+		default:
+			ret = 1;
+		}
+	} else {
+		ret = cvt_efmt(head, tail - head, prec, exp - 1, flags, fp);
+	}
+	freedtoa(head);
+	return ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+	static const struct dtoatestcase t[] = {
+#if defined(HAVE_MATH_H) && !defined(__vax__)
+		DTOATEST(INF),
+		DTOATEST(NAN),
+#else
+		DTOATEST(1.0/0.0),
+		DTOATEST(0.0/0.0),
+#endif
+		DTOATEST(0.0),
+		DTOATEST(1.0),
+		DTOATEST(10.0),
+		DTOATEST(100.0),
+		DTOATEST(1.1),
+		DTOATEST(1.123456789),
+		DTOATEST(11.23456789),
+		DTOATEST(112.3456789),
+		DTOATEST(1123.456789),
+		DTOATEST(11234.56789),
+		DTOATEST(112345.6789),
+		DTOATEST(1123456.789),
+		DTOATEST(11234567.89),
+		DTOATEST(112345678.9),
+		DTOATEST(1123456789.0),
+		DTOATEST(11234567890123.0),
+	};
+	size_t i, n;
+	int prec;
+	FILE *fp;
+	char *expect, *result;
+
+	for (i = 0; i < arraycount(t); ++i) {
+		for (prec = -1; prec <= 20; ++prec) {
+			expect = NULL;
+			if (asprintf(&expect, "%.*e", prec, t[i].dvalue) < 0)
+				abort();
+			result = NULL;
+			fp = open_memstream(&result, &n);
+			if (fp == NULL)
+				abort();
+			if (ecvt(t[i].dvalue, prec, fp))
+				abort();
+			fclose(fp);
+			printf("expect:[%s], result[%s]\n", expect, result);
+			if (strcmp(result, expect))
+				abort();
+			free(expect);
+			free(result);
+		}
+	}
+}
  1. 1
  2. 2
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.