Implement a better num2str()

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
diff --git a/Makefile b/Makefile
index 13baee8..04cb60f 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@
 
 OBJS += lib/rand.o
 OBJS += lib/flist_sort.o
+OBJS += lib/num2str.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
diff --git a/Makefile.FreeBSD b/Makefile.FreeBSD
index 5882961..e4adfca 100644
--- a/Makefile.FreeBSD
+++ b/Makefile.FreeBSD
@@ -10,6 +10,7 @@
 
 OBJS += lib/rand.o
 OBJS += lib/flist_sort.o
+OBJS += lib/num2str.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
diff --git a/Makefile.NetBSD b/Makefile.NetBSD
index c223ab2..9378b19 100644
--- a/Makefile.NetBSD
+++ b/Makefile.NetBSD
@@ -10,6 +10,7 @@
 
 OBJS += lib/rand.o
 OBJS += lib/flist_sort.o
+OBJS += lib/num2str.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
diff --git a/Makefile.aix b/Makefile.aix
index 83b5cf4..32d22a3 100644
--- a/Makefile.aix
+++ b/Makefile.aix
@@ -11,6 +11,7 @@
 
 OBJS += lib/rand.o
 OBJS += lib/getopt_long.o
+OBJS += lib/num2str.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
diff --git a/Makefile.mac b/Makefile.mac
index 77e5f03..952364a 100644
--- a/Makefile.mac
+++ b/Makefile.mac
@@ -10,6 +10,7 @@
 
 OBJS += lib/rand.o
 OBJS += lib/flist_sort.o
+OBJS += lib/num2str.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
diff --git a/Makefile.solaris b/Makefile.solaris
index 06020c2..e7ff53c 100644
--- a/Makefile.solaris
+++ b/Makefile.solaris
@@ -9,6 +9,7 @@
 
 OBJS += lib/rand.o
 OBJS += lib/flist_sort.o
+OBJS += lib/num2str.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
diff --git a/fio.h b/fio.h
index e8c025d..28104cd 100644
--- a/fio.h
+++ b/fio.h
@@ -568,6 +568,8 @@
 extern void options_mem_free(struct thread_data *);
 extern void td_fill_rand_seeds(struct thread_data *);
 extern void add_job_opts(const char **);
+extern char *num2str(unsigned long, int, int, int);
+
 #define FIO_GETOPT_JOB		0x89988998
 #define FIO_NR_OPTIONS		(FIO_MAX_OPTS + 128)
 
@@ -653,49 +655,6 @@
 	return 0;
 }
 
-/*
- * Cheesy number->string conversion, complete with carry rounding error.
- */
-static inline char *num2str(unsigned long num, int maxlen, int base, int pow2)
-{
-	char postfix[] = { ' ', 'K', 'M', 'G', 'P', 'E' };
-	unsigned int thousand;
-	char *buf;
-	int i;
-
-	if (pow2)
-		thousand = 1024;
-	else
-		thousand = 1000;
-
-	buf = malloc(128);
-
-	for (i = 0; base > 1; i++)
-		base /= thousand;
-
-	do {
-		int len, carry = 0;
-
-		len = sprintf(buf, "%'lu", num);
-		if (len <= maxlen) {
-			if (i >= 1) {
-				buf[len] = postfix[i];
-				buf[len + 1] = '\0';
-			}
-			return buf;
-		}
-
-		if ((num % thousand) >= (thousand / 2))
-			carry = 1;
-
-		num /= thousand;
-		num += carry;
-		i++;
-	} while (i <= 5);
-
-	return buf;
-}
-
 static inline int __should_check_rate(struct thread_data *td,
 				      enum fio_ddir ddir)
 {
diff --git a/lib/num2str.c b/lib/num2str.c
new file mode 100644
index 0000000..e114cdf
--- /dev/null
+++ b/lib/num2str.c
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * Cheesy number->string conversion, complete with carry rounding error.
+ */
+char *num2str(unsigned long num, int maxlen, int base, int pow2)
+{
+	char postfix[] = { ' ', 'K', 'M', 'G', 'P', 'E' };
+	unsigned int thousand[] = { 1000, 1024 };
+	unsigned int modulo, decimals;
+	int post_index;
+	char tmp[32], fmt[8];
+	char *buf;
+
+	buf = malloc(128);
+
+	for (post_index = 0; base > 1; post_index++)
+		base /= thousand[!!pow2];
+
+	modulo = -1U;
+	while (post_index < sizeof(postfix)) {
+		sprintf(tmp, "%lu", num);
+		if (strlen(tmp) <= maxlen)
+			break;
+
+		modulo = num % thousand[!!pow2];
+		num /= thousand[!!pow2];
+		post_index++;
+	}
+
+	if (modulo == -1U) {
+done:
+		sprintf(buf, "%lu%c", num, postfix[post_index]);
+		return buf;
+	}
+
+	sprintf(tmp, "%lu", num);
+	decimals = maxlen - strlen(tmp);
+	if (decimals <= 1)
+		goto done;
+
+	sprintf(fmt, "%%.%uu", decimals - 1);
+	sprintf(tmp, fmt, modulo);
+	sprintf(buf, "%lu.%s%c", num, tmp, postfix[post_index]);
+	return buf;
+}