ilib/strutils: fix rounding in size_to_human_string()
Thanks to bub75 for the idea. The patch adds SIZE_DECIMAL_2DIGITS into regression tests. Addresses: https://github.com/karelzak/util-linux/issues/998 Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
a890611357
commit
b38c20e13a
2 changed files with 58 additions and 37 deletions
|
@ -602,24 +602,41 @@ char *size_to_human_string(int options, uint64_t bytes)
|
|||
|
||||
/* round */
|
||||
if (frac) {
|
||||
/* get 3 digits after decimal point */
|
||||
frac = (frac * 1000) / (1ULL << exp);
|
||||
|
||||
if (options & SIZE_DECIMAL_2DIGITS) {
|
||||
frac = (frac / (1ULL << (exp - 10)) + 5) / 10;
|
||||
if (frac % 10 == 0)
|
||||
frac /= 10; /* convert N.90 to N.9 */
|
||||
/* round 4/5 and keep 2 digits after decimal point */
|
||||
frac = (frac + 5) / 10 ;
|
||||
} else {
|
||||
frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
|
||||
if (frac == 10)
|
||||
dec++, frac = 0;
|
||||
/* round 4/5 and keep 1 digit after decimal point */
|
||||
frac = ((frac + 50) / 100) * 10 ;
|
||||
}
|
||||
|
||||
/* rounding could have overflowed */
|
||||
if (frac == 100) {
|
||||
dec++;
|
||||
frac = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (frac) {
|
||||
struct lconv const *l = localeconv();
|
||||
char *dp = l ? l->decimal_point : NULL;
|
||||
int len;
|
||||
|
||||
if (!dp || !*dp)
|
||||
dp = ".";
|
||||
snprintf(buf, sizeof(buf), "%d%s%" PRIu64 "%s", dec, dp, frac, suffix);
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%d%s%02" PRIu64, dec, dp, frac);
|
||||
if (len > 0 && (size_t) len < sizeof(buf)) {
|
||||
/* remove potential extraneous zero */
|
||||
if (buf[len - 1] == '0')
|
||||
buf[len--] = '\0';
|
||||
/* append suffix */
|
||||
xstrncpy(buf+len, suffix, sizeof(buf) - len);
|
||||
} else
|
||||
*buf = '\0'; /* snprintf error */
|
||||
} else
|
||||
snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
|
||||
|
||||
|
@ -1051,7 +1068,7 @@ static int test_strdup_to_member(int argc, char *argv[])
|
|||
static int test_strutils_sizes(int argc, char *argv[])
|
||||
{
|
||||
uintmax_t size = 0;
|
||||
char *hum, *hum2;
|
||||
char *hum1, *hum2, *hum3;
|
||||
|
||||
if (argc < 2)
|
||||
return EXIT_FAILURE;
|
||||
|
@ -1059,13 +1076,17 @@ static int test_strutils_sizes(int argc, char *argv[])
|
|||
if (strtosize(argv[1], &size))
|
||||
errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
|
||||
|
||||
hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
|
||||
hum1 = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
|
||||
hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
|
||||
SIZE_SUFFIX_SPACE, size);
|
||||
hum3 = size_to_human_string(SIZE_SUFFIX_3LETTER |
|
||||
SIZE_SUFFIX_SPACE |
|
||||
SIZE_DECIMAL_2DIGITS, size);
|
||||
|
||||
printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
|
||||
free(hum);
|
||||
printf("%25s : %20ju : %8s : %12s : %13s\n", argv[1], size, hum1, hum2, hum3);
|
||||
free(hum1);
|
||||
free(hum2);
|
||||
free(hum3);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue