diff --git a/configure.ac b/configure.ac index d3f30aa..b4c5e3a 100644 --- a/configure.ac +++ b/configure.ac @@ -46,7 +46,7 @@ LIBBYTESIZE_PKG_CHECK_MODULES([PCRE2], [libpcre2-8]) AC_CHECK_LIB(gmp, __gmpz_init) -AC_CHECK_HEADERS([langinfo.h gmp.h mpfr.h stdint.h stdbool.h stdarg.h string.h stdio.h ctype.h], +AC_CHECK_HEADERS([langinfo.h gmp.h stdint.h stdbool.h stdarg.h string.h stdio.h ctype.h], [], [LIBBYTESIZE_SOFT_FAILURE([Header file $ac_header not found.])], []) diff --git a/dist/libbytesize.spec.in b/dist/libbytesize.spec.in index 1c350a8..5f50854 100644 --- a/dist/libbytesize.spec.in +++ b/dist/libbytesize.spec.in @@ -26,7 +26,6 @@ Source0: https://github.com/storaged-project/libbytesize/releases/download/% BuildRequires: make BuildRequires: gcc BuildRequires: gmp-devel -BuildRequires: mpfr-devel BuildRequires: pcre2-devel BuildRequires: gettext-devel %if %{with_python3} diff --git a/po/libbytesize.pot b/po/libbytesize.pot index 28f12d7..8300782 100644 --- a/po/libbytesize.pot +++ b/po/libbytesize.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: libbytesize 2.12\n" "Report-Msgid-Bugs-To: vtrefny@redhat.com\n" -"POT-Creation-Date: 2025-12-04 12:49+0100\n" +"POT-Creation-Date: 2025-12-17 12:02-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,86 +18,86 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" #. TRANSLATORS: 'B' for bytes -#: src/bs_size.c:52 src/bs_size.c:73 +#: src/bs_size.c:51 src/bs_size.c:72 msgid "B" msgstr "" #. TRANSLATORS: abbreviation for kibibyte, 2**10 bytes -#: src/bs_size.c:54 +#: src/bs_size.c:53 msgid "KiB" msgstr "" #. TRANSLATORS: abbreviation for mebibyte, 2**20 bytes -#: src/bs_size.c:56 +#: src/bs_size.c:55 msgid "MiB" msgstr "" #. TRANSLATORS: abbreviation for gibibyte, 2**30 bytes -#: src/bs_size.c:58 +#: src/bs_size.c:57 msgid "GiB" msgstr "" #. TRANSLATORS: abbreviation for tebibyte, 2**40 bytes -#: src/bs_size.c:60 +#: src/bs_size.c:59 msgid "TiB" msgstr "" #. TRANSLATORS: abbreviation for pebibyte, 2**50 bytes -#: src/bs_size.c:62 +#: src/bs_size.c:61 msgid "PiB" msgstr "" #. TRANSLATORS: abbreviation for exbibyte, 2**60 bytes -#: src/bs_size.c:64 +#: src/bs_size.c:63 msgid "EiB" msgstr "" #. TRANSLATORS: abbreviation for zebibyte, 2**70 bytes -#: src/bs_size.c:66 +#: src/bs_size.c:65 msgid "ZiB" msgstr "" #. TRANSLATORS: abbreviation for yobibyte, 2**80 bytes -#: src/bs_size.c:68 +#: src/bs_size.c:67 msgid "YiB" msgstr "" #. TRANSLATORS: abbreviation for kilobyte, 10**3 bytes -#: src/bs_size.c:75 +#: src/bs_size.c:74 msgid "KB" msgstr "" #. TRANSLATORS: abbreviation for megabyte, 10**6 bytes -#: src/bs_size.c:77 +#: src/bs_size.c:76 msgid "MB" msgstr "" #. TRANSLATORS: abbreviation for gigabyte, 10**9 bytes -#: src/bs_size.c:79 +#: src/bs_size.c:78 msgid "GB" msgstr "" #. TRANSLATORS: abbreviation for terabyte, 10**12 bytes -#: src/bs_size.c:81 +#: src/bs_size.c:80 msgid "TB" msgstr "" #. TRANSLATORS: abbreviation for petabyte, 10**15 bytes -#: src/bs_size.c:83 +#: src/bs_size.c:82 msgid "PB" msgstr "" #. TRANSLATORS: abbreviation for exabyte, 10**18 bytes -#: src/bs_size.c:85 +#: src/bs_size.c:84 msgid "EB" msgstr "" #. TRANSLATORS: abbreviation for zettabyte, 10**21 bytes -#: src/bs_size.c:87 +#: src/bs_size.c:86 msgid "ZB" msgstr "" #. TRANSLATORS: abbreviation for yottabyte, 10**24 bytes -#: src/bs_size.c:89 +#: src/bs_size.c:88 msgid "YB" msgstr "" diff --git a/src/Makefile.am b/src/Makefile.am index 9f68d12..ebbb749 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ LDADD = $(LIBINTL) lib_LTLIBRARIES = libbytesize.la libbytesize_la_CFLAGS = -Wall -Wextra -Werror -Wno-overflow -D_GNU_SOURCE -libbytesize_la_LIBADD = -lgmp -lmpfr $(PCRE2_LIBS) +libbytesize_la_LIBADD = -lgmp $(PCRE2_LIBS) libbytesize_la_LDFLAGS = -version-info 1:0:0 libbytesize_la_SOURCES = bs_size.c bs_size.h gettext.h diff --git a/src/bs_size.c b/src/bs_size.c index bf41180..e4f2ec8 100644 --- a/src/bs_size.c +++ b/src/bs_size.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -224,30 +223,66 @@ static void strstrip(char *str) { str[i-begin] = '\0'; } -static bool multiply_size_by_unit (mpfr_t size, char *unit_str) { +/* Helper function to convert mpf_t to mpz_t with round-toward-zero (truncate decimal) */ +static void mpz_set_f_round_zero(mpz_t rop, const mpf_t op) { + char *str; + const char *radix_char; + char *dot; + char *exp; + + /* Convert to decimal string with enough precision */ + gmp_asprintf(&str, "%.100Ff", op); + + /* Get the locale's decimal separator */ + radix_char = nl_langinfo(RADIXCHAR); + + /* Find decimal point (using locale's separator) and truncate */ + dot = strchr(str, radix_char[0]); + if (dot) { + *dot = '\0'; + } + + /* Remove any exponent notation */ + exp = strchr(str, 'e'); + if (!exp) exp = strchr(str, 'E'); + if (exp) *exp = '\0'; + + mpz_set_str(rop, str, 10); + free(str); +} + +static bool multiply_size_by_unit (mpf_t size, char *unit_str) { BSBunit bunit = BS_BUNIT_UNDEF; BSDunit dunit = BS_DUNIT_UNDEF; uint64_t pwr = 0; - mpfr_t dec_mul; + mpf_t dec_mul; + mpf_t base; size_t unit_str_len = 0; + uint64_t i; unit_str_len = strlen (unit_str); for (bunit=BS_BUNIT_B; bunit < BS_BUNIT_UNDEF; bunit++) if (strncasecmp (unit_str, b_units[bunit-BS_BUNIT_B], unit_str_len) == 0) { pwr = (uint64_t) bunit - BS_BUNIT_B; - mpfr_mul_2exp (size, size, 10 * pwr, MPFR_RNDN); + mpf_mul_2exp (size, size, 10 * pwr); return true; } - mpfr_init2 (dec_mul, BS_FLOAT_PREC_BITS); - mpfr_set_ui (dec_mul, 1000, MPFR_RNDN); + mpf_init2 (dec_mul, BS_FLOAT_PREC_BITS); + mpf_init2 (base, BS_FLOAT_PREC_BITS); + mpf_set_ui (base, 1000); for (dunit=BS_DUNIT_B; dunit < BS_DUNIT_UNDEF; dunit++) if (strncasecmp (unit_str, d_units[dunit-BS_DUNIT_B], unit_str_len) == 0) { pwr = (uint64_t) (dunit - BS_DUNIT_B); - mpfr_pow_ui (dec_mul, dec_mul, pwr, MPFR_RNDN); - mpfr_mul (size, size, dec_mul, MPFR_RNDN); - mpfr_clear (dec_mul); + /* Compute 1000^pwr using repeated multiplication */ + mpf_set_ui (dec_mul, 1); + for (i = 0; i < pwr; i++) { + mpf_mul (dec_mul, dec_mul, base); + } + mpf_mul (size, size, dec_mul); + mpf_clear (dec_mul); + mpf_clear (base); return true; } @@ -256,21 +291,27 @@ static bool multiply_size_by_unit (mpfr_t size, char *unit_str) { for (bunit=BS_BUNIT_B; bunit < BS_BUNIT_UNDEF; bunit++) if (strncasecmp (unit_str, _(b_units[bunit-BS_BUNIT_B]), unit_str_len) == 0) { pwr = (uint64_t) bunit - BS_BUNIT_B; - mpfr_mul_2exp (size, size, 10 * pwr, MPFR_RNDN); + mpf_mul_2exp (size, size, 10 * pwr); return true; } - mpfr_init2 (dec_mul, BS_FLOAT_PREC_BITS); - mpfr_set_ui (dec_mul, 1000, MPFR_RNDN); + mpf_set_ui (base, 1000); for (dunit=BS_DUNIT_B; dunit < BS_DUNIT_UNDEF; dunit++) if (strncasecmp (unit_str, _(d_units[dunit-BS_DUNIT_B]), unit_str_len) == 0) { pwr = (uint64_t) (dunit - BS_DUNIT_B); - mpfr_pow_ui (dec_mul, dec_mul, pwr, MPFR_RNDN); - mpfr_mul (size, size, dec_mul, MPFR_RNDN); - mpfr_clear (dec_mul); + /* Compute 1000^pwr using repeated multiplication */ + mpf_set_ui (dec_mul, 1); + for (i = 0; i < pwr; i++) { + mpf_mul (dec_mul, dec_mul, base); + } + mpf_mul (size, size, dec_mul); + mpf_clear (dec_mul); + mpf_clear (base); return true; } + mpf_clear (dec_mul); + mpf_clear (base); return false; } @@ -452,7 +493,7 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) { const char *radix_char = NULL; char *loc_size_str = NULL; mpf_t parsed_size; - mpfr_t size; + mpf_t size; int status = 0; BSSize ret = NULL; PCRE2_UCHAR *substring = NULL; @@ -518,8 +559,7 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) { return NULL; } - /* parse the number using GMP because it knows how to handle localization - much better than MPFR */ + /* parse the number using GMP - it handles localization correctly */ mpf_init2 (parsed_size, BS_FLOAT_PREC_BITS); status = mpf_set_str (parsed_size, *substring == '+' ? (const char *) substring+1 : (const char *) substring, 10); pcre2_substring_free (substring); @@ -531,9 +571,9 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) { mpf_clear (parsed_size); return NULL; } - /* but use MPFR from now on because GMP thinks 0.1*1000 = 99 */ - mpfr_init2 (size, BS_FLOAT_PREC_BITS); - mpfr_set_f (size, parsed_size, MPFR_RNDN); + /* Use GMP mpf_t directly - we'll handle rounding correctly when converting to integer */ + mpf_init2 (size, BS_FLOAT_PREC_BITS); + mpf_set (size, parsed_size); mpf_clear (parsed_size); status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "rest", &substring, &substring_len); @@ -545,7 +585,7 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) { pcre2_match_data_free (match_data); pcre2_code_free (regex); free (loc_size_str); - mpfr_clear (size); + mpf_clear (size); return NULL; } } @@ -554,10 +594,12 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) { pcre2_code_free (regex); ret = bs_size_new (); - mpfr_get_z (ret->bytes, size, MPFR_RNDZ); + /* Convert from mpf_t to mpz_t with round-toward-zero using string conversion + to ensure correct decimal rounding (mpz_set_f truncates binary, giving wrong results) */ + mpz_set_f_round_zero (ret->bytes, size); free (loc_size_str); - mpfr_clear (size); + mpf_clear (size); return ret; } diff --git a/src/bytesize.pc.in b/src/bytesize.pc.in index 74762bb..fbd2f00 100644 --- a/src/bytesize.pc.in +++ b/src/bytesize.pc.in @@ -9,4 +9,4 @@ URL: https://github.com/storaged-project/libbytesize Version: @VERSION@ Cflags: -I${includedir}/bytesize Libs: -lbytesize -Libs.private: -lgmp -lmpfr +Libs.private: -lgmp