]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
ilib/strutils: fix rounding in size_to_human_string()
authorKarel Zak <kzak@redhat.com>
Wed, 15 Apr 2020 13:01:12 +0000 (15:01 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 15 Apr 2020 13:06:37 +0000 (15:06 +0200)
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>
lib/strutils.c
tests/expected/misc/strtosize

index be404e703bb0ad69743f36c9524ff0cbaf372e8b..ccf48919bdc1fb82e7bdba1e08cdbe4f79071989 100644 (file)
@@ -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;
 }
index 8d93e142d3b0c3fb44dadf886ea30491de3c9293..abda45a57588b5cc0c75a1b9657426fa1bf4ec63 100644 (file)
@@ -1,26 +1,26 @@
-                        0 :                    0 :       0B :          0 B
-                        1 :                    1 :       1B :          1 B
-                      123 :                  123 :     123B :        123 B
-     18446744073709551615 : 18446744073709551615 :      16E :       16 EiB
-                       1K :                 1024 :       1K :        1 KiB
-                     1KiB :                 1024 :       1K :        1 KiB
-                       1M :              1048576 :       1M :        1 MiB
-                     1MiB :              1048576 :       1M :        1 MiB
-                       1G :           1073741824 :       1G :        1 GiB
-                     1GiB :           1073741824 :       1G :        1 GiB
-                       1T :        1099511627776 :       1T :        1 TiB
-                     1TiB :        1099511627776 :       1T :        1 TiB
-                       1P :     1125899906842624 :       1P :        1 PiB
-                     1PiB :     1125899906842624 :       1P :        1 PiB
-                       1E :  1152921504606846976 :       1E :        1 EiB
-                     1EiB :  1152921504606846976 :       1E :        1 EiB
-                      1KB :                 1000 :    1000B :       1000 B
-                      1MB :              1000000 :   976.6K :    976.6 KiB
-                      1GB :           1000000000 :   953.7M :    953.7 MiB
-                      1TB :        1000000000000 :   931.3G :    931.3 GiB
-                      1PB :     1000000000000000 :   909.5T :    909.5 TiB
-                      1EB :  1000000000000000000 :   888.2P :    888.2 PiB
-                        1 :                    1 :       1B :          1 B
-                     0x0a :                   10 :      10B :         10 B
-                   0xff00 :                65280 :    63.8K :     63.8 KiB
-               0x80000000 :           2147483648 :       2G :        2 GiB
+                        0 :                    0 :       0B :          0 B :           0 B
+                        1 :                    1 :       1B :          1 B :           1 B
+                      123 :                  123 :     123B :        123 B :         123 B
+     18446744073709551615 : 18446744073709551615 :      15E :       15 EiB :     15.01 EiB
+                       1K :                 1024 :       1K :        1 KiB :         1 KiB
+                     1KiB :                 1024 :       1K :        1 KiB :         1 KiB
+                       1M :              1048576 :       1M :        1 MiB :         1 MiB
+                     1MiB :              1048576 :       1M :        1 MiB :         1 MiB
+                       1G :           1073741824 :       1G :        1 GiB :         1 GiB
+                     1GiB :           1073741824 :       1G :        1 GiB :         1 GiB
+                       1T :        1099511627776 :       1T :        1 TiB :         1 TiB
+                     1TiB :        1099511627776 :       1T :        1 TiB :         1 TiB
+                       1P :     1125899906842624 :       1P :        1 PiB :         1 PiB
+                     1PiB :     1125899906842624 :       1P :        1 PiB :         1 PiB
+                       1E :  1152921504606846976 :       1E :        1 EiB :         1 EiB
+                     1EiB :  1152921504606846976 :       1E :        1 EiB :         1 EiB
+                      1KB :                 1000 :    1000B :       1000 B :        1000 B
+                      1MB :              1000000 :   976.6K :    976.6 KiB :    976.56 KiB
+                      1GB :           1000000000 :   953.7M :    953.7 MiB :    953.67 MiB
+                      1TB :        1000000000000 :   931.3G :    931.3 GiB :    931.32 GiB
+                      1PB :     1000000000000000 :   909.5T :    909.5 TiB :    909.49 TiB
+                      1EB :  1000000000000000000 :   888.2P :    888.2 PiB :    888.18 PiB
+                        1 :                    1 :       1B :          1 B :           1 B
+                     0x0a :                   10 :      10B :         10 B :          10 B
+                   0xff00 :                65280 :    63.8K :     63.8 KiB :     63.75 KiB
+               0x80000000 :           2147483648 :       2G :        2 GiB :         2 GiB