diff options
author | Karel Zak | 2019-05-13 17:07:14 +0200 |
---|---|---|
committer | Karel Zak | 2019-05-13 17:07:14 +0200 |
commit | 8c368dc6d3771d4655c953c42ff009f20d51de1f (patch) | |
tree | ff4c8bf0697c12ac198e1c61d7b7674d4da1bb75 /lib | |
parent | lib/strutils: parse_size() fix frac with zeros (diff) | |
download | kernel-qcow2-util-linux-8c368dc6d3771d4655c953c42ff009f20d51de1f.tar.gz kernel-qcow2-util-linux-8c368dc6d3771d4655c953c42ff009f20d51de1f.tar.xz kernel-qcow2-util-linux-8c368dc6d3771d4655c953c42ff009f20d51de1f.zip |
lib/strutils: parse_size() fix frac digit calculation
Old code:
./test_strutils --size 0.5MiB
0.5MiB : 512000 : 500K : 500 KiB
./test_strutils --size 0.50MiB
0.50MiB : 5120000 : 4.9M : 4.9 MiB
New code:
./test_strutils --size 0.5MiB
0.5MiB : 524288 : 512K : 512 KiB
./test_strutils --size 0.50MiB
0.50MiB : 524288 : 512K : 512 KiB
Note that the new implementation also does not use float points,
because we need to support PiB and so on... it seems good enough for
things like:
./test_strutils --size 7.13G
7.13G : 7656104581 : 7.1G : 7.1 GiB
./test_strutils --size 7.16G
7.16G : 7690675814 : 7.2G : 7.2 GiB
to avoid situation where cfdisk creates partition with completely
crazy numbers.
Addresses: https://github.com/karelzak/util-linux/issues/782
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/strutils.c | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/lib/strutils.c b/lib/strutils.c index 327bf37bc..8076574c5 100644 --- a/lib/strutils.c +++ b/lib/strutils.c @@ -162,17 +162,39 @@ check_suffix: if (power) *power = pwr; if (frac && pwr) { - int zeros_in_pwr = frac_zeros % 3; - int frac_pwr = pwr - (frac_zeros / 3) - 1; - uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 : - zeros_in_pwr == 1 ? 10 : 1); + int i; + uintmax_t frac_div = 10, frac_poz = 1, frac_base = 1; - if (frac_pwr < 0) { - rc = -EINVAL; - goto err; - } - do_scale_by_power(&y, base, frac_pwr); - x += y; + /* mega, giga, ... */ + do_scale_by_power(&frac_base, base, pwr); + + /* maximal divisor for last digit (e.g. for 0.05 is + * frac_div=100, for 0.054 is frac_div=1000, etc.) + */ + while (frac_div < frac) + frac_div *= 10; + + /* 'frac' is without zeros (5 means 0.5 as well as 0.05) */ + for (i = 0; i < frac_zeros; i++) + frac_div *= 10; + + /* + * Go backwardly from last digit and add to result what the + * digit represents in the frac_base. For example 0.25G + * + * 5 means 1GiB / (100/5) + * 2 means 1GiB / (10/2) + */ + do { + unsigned int seg = frac % 10; /* last digit of the frac */ + uintmax_t seg_div = frac_div / frac_poz; /* what represents the segment 1000, 100, .. */ + + frac /= 10; /* remove last digit from frac */ + frac_poz *= 10; + + if (seg) + x += frac_base / (seg_div / seg); + } while (frac); } done: *res = x; |