From: kusumi.tomohiro@gmail.com
To: axboe@kernel.dk, fio@vger.kernel.org
Cc: Tomohiro Kusumi <tkusumi@tuxera.com>
Subject: [PATCH 8/8] Fix num2str() output when modulo != -1U
Date: Tue, 4 Apr 2017 23:22:18 +0300 [thread overview]
Message-ID: <20170404202218.52260-8-tkusumi@tuxera.com> (raw)
In-Reply-To: <20170404202218.52260-1-tkusumi@tuxera.com>
From: Tomohiro Kusumi <tkusumi@tuxera.com>
I'm not sure what 05463816 actually fixed (as it only says "fix"),
but this isn't properly working at least for some input numbers unless
".%s" part of sprintf() is meant to be something other than decimal
part of the input number.
This commit might be breaking something that 05463816 had fixed,
which then needs to be rejected, but it at least prints decimal number
as expected. For example, when 12345 is 12.0556640625 KiB, and 23456
is 22.90625 KiB,
# python -c "print(12345.0 / 1024)"
12.0556640625
# python -c "print(23456.0 / 1024)"
22.90625
running this code as well as fio result in
# cat ./test1.c
#include <stdio.h>
#include "lib/num2str.h"
int main(void) {
printf("%s-%s\n",
num2str(12345, 4, 1, 1, N2S_BYTE),
num2str(23456, 4, 1, 1, N2S_BYTE));
return 0;
}
# gcc -Wall -g -DCONFIG_STATIC_ASSERT ./test1.c ./lib/num2str.c
below with the current implementation
# ./a.out
12.6KiB-22.1KiB
# ./fio --name=xxxxx --ioengine=sync --rw=read --size=1m --unlink=1 --bsrange=12345:23456 | grep "rw=read"
xxxxx: (g=0): rw=read, bs=(R) 12.6KiB-22.1KiB, (W) 12.6KiB-22.1KiB, (T) 12.6KiB-22.1KiB, ioengine=sync, iodepth=1
whereas below with this commit.
# ./a.out
12.1KiB-22.9KiB
# ./fio --name=xxxxx --ioengine=sync --rw=read --size=1m --unlink=1 --bsrange=12345:23456 | grep "rw=read"
xxxxx: (g=0): rw=read, bs=(R) 12.1KiB-22.9KiB, (W) 12.1KiB-22.9KiB, (T) 12.1KiB-22.9KiB, ioengine=sync, iodepth=1
-- some more verification with various parameters
# cat ./testx.c
#include <stdio.h>
#include "lib/num2str.h"
static void f(uint64_t n, int maxlen, int base, int pow2, int units) {
printf("%10jd %s\n", n, num2str(n, maxlen, base, pow2, units));
}
int main(void) {
/* 1 */
f(1, 1, 1, 1, N2S_BYTE);
f(1, 1, 1, 0, N2S_BYTE);
printf("====================\n");
/* 10 */
f(10, 2, 1, 1, N2S_BYTE);
f(10, 2, 1, 0, N2S_BYTE);
printf("====================\n");
/* 1000 */
f(1000, 5, 1, 1, N2S_BYTE);
f(1000, 5, 1, 0, N2S_BYTE);
f(1000, 4, 1, 1, N2S_BYTE);
f(1000, 4, 1, 0, N2S_BYTE);
f(1000, 3, 1, 1, N2S_BYTE);
f(1000, 3, 1, 0, N2S_BYTE);
f(1000, 2, 1, 1, N2S_BYTE);
f(1000, 2, 1, 0, N2S_BYTE);
f(1000, 1, 1, 1, N2S_BYTE);
f(1000, 1, 1, 0, N2S_BYTE);
printf("====================\n");
/* 1024 */
f(1024, 5, 1, 1, N2S_BYTE);
f(1024, 5, 1, 0, N2S_BYTE);
f(1024, 4, 1, 1, N2S_BYTE);
f(1024, 4, 1, 0, N2S_BYTE);
f(1024, 3, 1, 1, N2S_BYTE);
f(1024, 3, 1, 0, N2S_BYTE);
f(1024, 2, 1, 1, N2S_BYTE);
f(1024, 2, 1, 0, N2S_BYTE);
f(1024, 1, 1, 1, N2S_BYTE);
f(1024, 1, 1, 0, N2S_BYTE);
printf("====================\n");
/* 12345 */
f(12345, 6, 1, 1, N2S_BYTE);
f(12345, 6, 1, 0, N2S_BYTE);
f(12345, 5, 1, 1, N2S_BYTE);
f(12345, 5, 1, 0, N2S_BYTE);
f(12345, 4, 1, 1, N2S_BYTE);
f(12345, 4, 1, 0, N2S_BYTE);
f(12345, 3, 1, 1, N2S_BYTE);
f(12345, 3, 1, 0, N2S_BYTE);
f(12345, 2, 1, 1, N2S_BYTE);
f(12345, 2, 1, 0, N2S_BYTE);
f(12345, 1, 1, 1, N2S_BYTE);
f(12345, 1, 1, 0, N2S_BYTE);
printf("====================\n");
/* 1234567 */
f(1234567, 8, 1, 1, N2S_BYTE);
f(1234567, 8, 1, 0, N2S_BYTE);
f(1234567, 7, 1, 1, N2S_BYTE);
f(1234567, 7, 1, 0, N2S_BYTE);
f(1234567, 6, 1, 1, N2S_BYTE);
f(1234567, 6, 1, 0, N2S_BYTE);
f(1234567, 5, 1, 1, N2S_BYTE);
f(1234567, 5, 1, 0, N2S_BYTE);
f(1234567, 4, 1, 1, N2S_BYTE);
f(1234567, 4, 1, 0, N2S_BYTE);
f(1234567, 3, 1, 1, N2S_BYTE);
f(1234567, 3, 1, 0, N2S_BYTE);
f(1234567, 2, 1, 1, N2S_BYTE);
f(1234567, 2, 1, 0, N2S_BYTE);
f(1234567, 1, 1, 1, N2S_BYTE);
f(1234567, 1, 1, 0, N2S_BYTE);
printf("====================\n");
/* 1234567 with base 1024 */
f(1234567, 8, 1024, 1, N2S_BYTE);
f(1234567, 8, 1024, 0, N2S_BYTE);
f(1234567, 7, 1024, 1, N2S_BYTE);
f(1234567, 7, 1024, 0, N2S_BYTE);
f(1234567, 6, 1024, 1, N2S_BYTE);
f(1234567, 6, 1024, 0, N2S_BYTE);
f(1234567, 5, 1024, 1, N2S_BYTE);
f(1234567, 5, 1024, 0, N2S_BYTE);
f(1234567, 4, 1024, 1, N2S_BYTE);
f(1234567, 4, 1024, 0, N2S_BYTE);
f(1234567, 3, 1024, 1, N2S_BYTE);
f(1234567, 3, 1024, 0, N2S_BYTE);
f(1234567, 2, 1024, 1, N2S_BYTE);
f(1234567, 2, 1024, 0, N2S_BYTE);
f(1234567, 1, 1024, 1, N2S_BYTE);
f(1234567, 1, 1024, 0, N2S_BYTE);
printf("====================\n");
/* 1234567 with base 1024 with N2S_BIT */
f(1234567, 9, 1024, 1, N2S_BIT);
f(1234567, 9, 1024, 0, N2S_BIT);
f(1234567, 8, 1024, 1, N2S_BIT);
f(1234567, 8, 1024, 0, N2S_BIT);
f(1234567, 7, 1024, 1, N2S_BIT);
f(1234567, 7, 1024, 0, N2S_BIT);
f(1234567, 6, 1024, 1, N2S_BIT);
f(1234567, 6, 1024, 0, N2S_BIT);
f(1234567, 5, 1024, 1, N2S_BIT);
f(1234567, 5, 1024, 0, N2S_BIT);
f(1234567, 4, 1024, 1, N2S_BIT);
f(1234567, 4, 1024, 0, N2S_BIT);
f(1234567, 3, 1024, 1, N2S_BIT);
f(1234567, 3, 1024, 0, N2S_BIT);
f(1234567, 2, 1024, 1, N2S_BIT);
f(1234567, 2, 1024, 0, N2S_BIT);
f(1234567, 1, 1024, 1, N2S_BIT);
f(1234567, 1, 1024, 0, N2S_BIT);
printf("====================\n");
return 0;
}
# gcc -Wall -g -DCONFIG_STATIC_ASSERT ./testx.c ./lib/num2str.c
# ./a.out
1 1B
1 1B
====================
10 10B
10 10B
====================
1000 1000B
1000 1000B
1000 1000B
1000 1000B
1000 0.0KiB
1000 1.0kB
1000 1KiB
1000 1kB
1000 1KiB
1000 1kB
====================
1024 1024B
1024 1024B
1024 1024B
1024 1024B
1024 1.0KiB
1024 1.0kB
1024 1KiB
1024 1kB
1024 1KiB
1024 1kB
====================
12345 12345B
12345 12345B
12345 12345B
12345 12345B
12345 12.1KiB
12345 12.3kB
12345 12KiB
12345 12kB
12345 12KiB
12345 12kB
12345 0MiB
12345 0MB
====================
1234567 1234567B
1234567 1234567B
1234567 1234567B
1234567 1234567B
1234567 1205.6KiB
1234567 1234.6kB
1234567 1206KiB
1234567 1235kB
1234567 1206KiB
1234567 1235kB
1234567 1.2MiB
1234567 1.2MB
1234567 1MiB
1234567 1MB
1234567 1MiB
1234567 1MB
====================
1234567 1234567KiB
1234567 1234567kB
1234567 1234567KiB
1234567 1234567kB
1234567 1205.6MiB
1234567 1234.6MB
1234567 1206MiB
1234567 1235MB
1234567 1206MiB
1234567 1235MB
1234567 1.2GiB
1234567 1.2GB
1234567 1GiB
1234567 1GB
1234567 1GiB
1234567 1GB
====================
1234567 9876536Kibit
1234567 9876536kbit
1234567 9876536Kibit
1234567 9876536kbit
1234567 9876536Kibit
1234567 9876536kbit
1234567 9645.1Mibit
1234567 9876.5Mbit
1234567 9645Mibit
1234567 9877Mbit
1234567 9645Mibit
1234567 9877Mbit
1234567 9.4Gibit
1234567 9.9Gbit
1234567 9Gibit
1234567 10Gbit
1234567 9Gibit
1234567 10Gbit
====================
Signed-off-by: Tomohiro Kusumi <tkusumi@tuxera.com>
---
lib/num2str.c | 34 +++++++++++++++++++++-------------
1 file changed, 21 insertions(+), 13 deletions(-)
diff --git a/lib/num2str.c b/lib/num2str.c
index 448d3ff..8d08841 100644
--- a/lib/num2str.c
+++ b/lib/num2str.c
@@ -10,7 +10,7 @@
/**
* num2str() - Cheesy number->string conversion, complete with carry rounding error.
* @num: quantity (e.g., number of blocks, bytes or bits)
- * @maxlen: max number of digits in the output string (not counting prefix and units)
+ * @maxlen: max number of digits in the output string (not counting prefix and units, but counting .)
* @base: multiplier for num (e.g., if num represents Ki, use 1024)
* @pow2: select unit prefix - 0=power-of-10 decimal SI, nonzero=power-of-2 binary IEC
* @units: select units - N2S_* macros defined in num2str.h
@@ -23,9 +23,9 @@ char *num2str(uint64_t num, int maxlen, int base, int pow2, int units)
const char **unitprefix;
const char *unitstr[] = { "", "/s", "B", "bit", "B/s", "bit/s" };
const unsigned int thousand[] = { 1000, 1024 };
- unsigned int modulo, decimals;
+ unsigned int modulo;
int unit_index = 0, post_index, carry = 0;
- char tmp[32];
+ char tmp[32], fmt[32];
char *buf;
compiletime_assert(sizeof(sistr) == sizeof(iecstr), "unit prefix arrays must be identical sizes");
@@ -62,6 +62,9 @@ char *num2str(uint64_t num, int maxlen, int base, int pow2, int units)
break;
}
+ /*
+ * Divide by K/Ki until string length of num <= maxlen.
+ */
modulo = -1U;
while (post_index < sizeof(sistr)) {
sprintf(tmp, "%llu", (unsigned long long) num);
@@ -74,6 +77,9 @@ char *num2str(uint64_t num, int maxlen, int base, int pow2, int units)
post_index++;
}
+ /*
+ * If no modulo, then we're done.
+ */
if (modulo == -1U) {
done:
if (post_index >= ARRAY_SIZE(sistr))
@@ -84,23 +90,25 @@ done:
return buf;
}
+ /*
+ * If no room for decimals, then we're done.
+ */
sprintf(tmp, "%llu", (unsigned long long) num);
- decimals = maxlen - strlen(tmp);
- if ((int)decimals <= 1) {
+ if ((int)(maxlen - strlen(tmp)) <= 1) {
if (carry)
num++;
goto done;
}
- do {
- sprintf(tmp, "%u", modulo);
- if (strlen(tmp) <= decimals - 1)
- break;
-
- modulo = (modulo + 9) / 10;
- } while (1);
+ /*
+ * Fill in everything and return the result.
+ */
+ assert(maxlen - strlen(tmp) - 1 > 0);
+ assert(modulo < thousand[!!pow2]);
+ sprintf(fmt, "%%.%df", (int)(maxlen - strlen(tmp) - 1));
+ sprintf(tmp, fmt, (double)modulo / (double)thousand[!!pow2]);
- sprintf(buf, "%llu.%u%s%s", (unsigned long long) num, modulo,
+ sprintf(buf, "%llu.%s%s%s", (unsigned long long) num, &tmp[2],
unitprefix[post_index], unitstr[unit_index]);
return buf;
}
--
2.9.3
next prev parent reply other threads:[~2017-04-04 20:22 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-04-04 20:22 [PATCH 1/8] Don't silently terminate td when no I/O performed due to error kusumi.tomohiro
2017-04-04 20:22 ` [PATCH 2/8] dump_td_info() doesn't really need to be a function kusumi.tomohiro
2017-04-04 20:22 ` [PATCH 3/8] Add assert(0) to DDIR_DATASYNC sync path if fdatasync(2) is unsupported kusumi.tomohiro
2017-04-08 17:02 ` Jens Axboe
2017-04-04 20:22 ` [PATCH 4/8] Make lib/prio_tree.c a stand-alone library kusumi.tomohiro
2017-04-04 20:22 ` [PATCH 5/8] Make lib/memalign.c " kusumi.tomohiro
2017-04-04 20:22 ` [PATCH 6/8] Make lib/num2str.c a stand-alone library by adding lib/num2str.h kusumi.tomohiro
2017-04-04 20:22 ` [PATCH 7/8] Fix num2str() output when maxlen <= strlen(tmp) kusumi.tomohiro
2017-04-04 20:22 ` kusumi.tomohiro [this message]
2017-04-04 20:46 ` [PATCH 8/8] Fix num2str() output when modulo != -1U Elliott, Robert (Persistent Memory)
2017-04-08 17:05 ` [PATCH 1/8] Don't silently terminate td when no I/O performed due to error Jens Axboe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170404202218.52260-8-tkusumi@tuxera.com \
--to=kusumi.tomohiro@gmail.com \
--cc=axboe@kernel.dk \
--cc=fio@vger.kernel.org \
--cc=tkusumi@tuxera.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.