>From 49971cf38e6caef5e675a9a7f196e67c99a085c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Mon, 22 Jun 2015 04:19:48 +0100 Subject: [PATCH] numfmt: don't hardcode floating point limits and precision * src/numfmt.c (MAX_UNSCALED_DIGITS): Set this to LDBL_DIG rather than hardcoding at 18 for better portability. * tests/misc/numfmt.pl: Restrict limit tests to supported platforms. --- src/numfmt.c | 13 +- tests/misc/numfmt.pl | 352 ++++++++++++++++++++++++++------------------------- 2 files changed, 188 insertions(+), 177 deletions(-) diff --git a/src/numfmt.c b/src/numfmt.c index 1a7185f..4af16fa 100644 --- a/src/numfmt.c +++ b/src/numfmt.c @@ -153,7 +153,7 @@ enum { DELIMITER_DEFAULT = CHAR_MAX + 1 }; /* Maximum number of digits we can safely handle without precision loss, if scaling is 'none'. */ -enum { MAX_UNSCALED_DIGITS = 18 }; +enum { MAX_UNSCALED_DIGITS = LDBL_DIG }; /* Maximum number of digits we can work with. This is equivalent to 999Y. @@ -589,7 +589,7 @@ simple_strtod_float (const char *input_str, Returns: SSE_OK - valid number. - SSE_OK_PRECISION_LOSS - if more than 18 digits were used. + SSE_OK_PRECISION_LOSS - if more than LDBL_DIG digits were used. SSE_OVERFLOW - if more than 27 digits (999Y) were used. SSE_INVALID_NUMBER - if no digits were found. SSE_VALID_BUT_FORBIDDEN_SUFFIX @@ -604,9 +604,12 @@ simple_strtod_human (const char *input_str, /* 'scale_auto' is checked below. */ int scale_base = default_scale_base (allowed_scaling); - devmsg ("simple_strtod_human:\n input string: %s\n " - "locale decimal-point: %s\n", - quote_n (0, input_str), quote_n (1, decimal_point)); + devmsg ("simple_strtod_human:\n input string: %s\n" + " locale decimal-point: %s\n" + " MAX_UNSCALED_DIGITS: %d\n", + quote_n (0, input_str), + quote_n (1, decimal_point), + MAX_UNSCALED_DIGITS); enum simple_strtod_error e = simple_strtod_float (input_str, endptr, value, precision); diff --git a/tests/misc/numfmt.pl b/tests/misc/numfmt.pl index a6432a7..fcda1ce 100755 --- a/tests/misc/numfmt.pl +++ b/tests/misc/numfmt.pl @@ -102,8 +102,8 @@ my @Tests = ['unit-7.3', '--from-unit=1i 0', {ERR => "$prog: invalid unit size: '1i'\n"}, {EXIT => '1'}], - ['unit-8', '--from-unit=1234567890123456789012345 --to=iec 30', - {ERR => "$prog: invalid unit size: '1234567890123456789012345'\n"}, + ['unit-8', '--from-unit='.$limits->{UINTMAX_OFLOW}.' --to=iec 30', + {ERR => "$prog: invalid unit size: '$limits->{UINTMAX_OFLOW}'\n"}, {EXIT => '1'}], ['unit-9', '--from-unit=0 1', {ERR => "$prog: invalid unit size: '0'\n"}, @@ -361,15 +361,6 @@ my @Tests = {ERR=>"$prog: invalid number: ''\n"}, {EXIT=> 2}], - # INTEGRAL_OVERFLOW - ['strtod-3', '--from=si "1234567890123456789012345678901234567890'. - '1234567890123456789012345678901234567890"', - {ERR=>"$prog: value too large to be converted: '" . - "1234567890123456789012345678901234567890" . - "1234567890123456789012345678901234567890'\n", - }, - {EXIT=> 2}], - # FRACTION_NO_DIGITS_FOUND ['strtod-5', '--from=si 12.', {ERR=>"$prog: invalid number: '12.'\n"}, @@ -383,15 +374,6 @@ my @Tests = {ERR=>"$prog: invalid number: '12. 2'\n"}, {EXIT=>2}], - # FRACTION_OVERFLOW - ['strtod-7', '--from=si "12.1234567890123456789012345678901234567890'. - '1234567890123456789012345678901234567890"', - {ERR=>"$prog: value too large to be converted: '" . - "12.1234567890123456789012345678901234567890" . - "1234567890123456789012345678901234567890'\n", - }, - {EXIT=> 2}], - # INVALID_SUFFIX ['strtod-9', '--from=si 12.2Q', {ERR=>"$prog: invalid suffix in input: '12.2Q'\n"}, @@ -527,138 +509,6 @@ my @Tests = {OUT=>"5.8594\n-5.8594"}], - # Large Values - ['large-1','1000000000000000', {OUT=>"1000000000000000"}], - # 18 digits is OK - ['large-2','1000000000000000000', {OUT=>"1000000000000000000"}], - # 19 digits is too much (without output scaling) - ['large-3','10000000000000000000', - {ERR => "$prog: value too large to be printed: '1e+19' " . - "(consider using --to)\n"}, - {EXIT=>2}], - ['large-4','1000000000000000000.0', - {ERR => "$prog: value/precision too large to be printed: " . - "'1e+18/1' (consider using --to)\n"}, - {EXIT=>2}], - - - # Test input: - # Up to 27 digits is OK. - ['large-3.1', '--to=si 1', {OUT=> "1"}], - ['large-3.2', '--to=si 10', {OUT=> "10"}], - ['large-3.3', '--to=si 100', {OUT=> "100"}], - ['large-3.4', '--to=si 1000', {OUT=>"1.0K"}], - ['large-3.5', '--to=si 10000', {OUT=> "10K"}], - ['large-3.6', '--to=si 100000', {OUT=>"100K"}], - ['large-3.7', '--to=si 1000000', {OUT=>"1.0M"}], - ['large-3.8', '--to=si 10000000', {OUT=> "10M"}], - ['large-3.9', '--to=si 100000000', {OUT=>"100M"}], - ['large-3.10','--to=si 1000000000', {OUT=>"1.0G"}], - ['large-3.11','--to=si 10000000000', {OUT=> "10G"}], - ['large-3.12','--to=si 100000000000', {OUT=>"100G"}], - ['large-3.13','--to=si 1000000000000', {OUT=>"1.0T"}], - ['large-3.14','--to=si 10000000000000', {OUT=> "10T"}], - ['large-3.15','--to=si 100000000000000', {OUT=>"100T"}], - ['large-3.16','--to=si 1000000000000000', {OUT=>"1.0P"}], - ['large-3.17','--to=si 10000000000000000', {OUT=> "10P"}], - ['large-3.18','--to=si 100000000000000000', {OUT=>"100P"}], - ['large-3.19','--to=si 1000000000000000000', {OUT=>"1.0E"}], - ['large-3.20','--to=si 10000000000000000000', {OUT=> "10E"}], - ['large-3.21','--to=si 210000000000000000000', {OUT=>"210E"}], - ['large-3.22','--to=si 3210000000000000000000', {OUT=>"3.3Z"}], - ['large-3.23','--to=si 43210000000000000000000', {OUT=> "44Z"}], - ['large-3.24','--to=si 543210000000000000000000', {OUT=>"544Z"}], - ['large-3.25','--to=si 6543210000000000000000000', {OUT=>"6.6Y"}], - ['large-3.26','--to=si 76543210000000000000000000', {OUT=> "77Y"}], - ['large-3.27','--to=si 876543210000000000000000000', {OUT=>"877Y"}], - - # More than 27 digits is not OK - ['large-3.28','--to=si 9876543210000000000000000000', - {ERR => "$prog: value too large to be converted: " . - "'9876543210000000000000000000'\n"}, - {EXIT => 2}], - - # Test Output - ['large-4.1', '--from=si 9.7M', {OUT=>"9700000"}], - ['large-4.2', '--from=si 10M', {OUT =>"10000000"}], - ['large-4.3', '--from=si 200M', {OUT =>"200000000"}], - ['large-4.4', '--from=si 3G', {OUT =>"3000000000"}], - ['large-4.5', '--from=si 40G', {OUT =>"40000000000"}], - ['large-4.6', '--from=si 500G', {OUT =>"500000000000"}], - ['large-4.7', '--from=si 6T', {OUT =>"6000000000000"}], - ['large-4.8', '--from=si 70T', {OUT =>"70000000000000"}], - ['large-4.9', '--from=si 800T', {OUT =>"800000000000000"}], - ['large-4.10','--from=si 9P', {OUT =>"9000000000000000"}], - ['large-4.11','--from=si 10P', {OUT =>"10000000000000000"}], - ['large-4.12','--from=si 200P', {OUT =>"200000000000000000"}], - ['large-4.13','--from=si 3E', {OUT =>"3000000000000000000"}], - - # More than 18 digits of output without scaling - no good. - ['large-4.14','--from=si 40E', - {ERR => "$prog: value too large to be printed: '4e+19' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.15','--from=si 500E', - {ERR => "$prog: value too large to be printed: '5e+20' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.16','--from=si 6Z', - {ERR => "$prog: value too large to be printed: '6e+21' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.17','--from=si 70Z', - {ERR => "$prog: value too large to be printed: '7e+22' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.18','--from=si 800Z', - {ERR => "$prog: value too large to be printed: '8e+23' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.19','--from=si 9Y', - {ERR => "$prog: value too large to be printed: '9e+24' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.20','--from=si 10Y', - {ERR => "$prog: value too large to be printed: '1e+25' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-4.21','--from=si 200Y', - {ERR => "$prog: value too large to be printed: '2e+26' " . - "(consider using --to)\n"}, - {EXIT => 2}], - - ['large-5.1','--to=si 1000000000000000000', {OUT=>"1.0E"}], - ['large-5','--from=si --to=si 2E', {OUT=>"2.0E"}], - ['large-6','--from=si --to=si 3.4Z', {OUT=>"3.4Z"}], - ['large-7','--from=si --to=si 80Y', {OUT=>"80Y"}], - ['large-8','--from=si --to=si 9000Z', {OUT=>"9.0Y"}], - - ['large-10','--from=si --to=si 999Y', {OUT=>"999Y"}], - ['large-11','--from=si --to=iec 999Y', {OUT=>"827Y"}], - ['large-12','--from=si --round=down --to=iec 999Y', {OUT=>"826Y"}], - - # units can also affect the output - ['large-13','--from=si --from-unit=1000000 9P', - {ERR => "$prog: value too large to be printed: '9e+21' " . - "(consider using --to)\n"}, - {EXIT => 2}], - ['large-13.1','--from=si --from-unit=1000000 --to=si 9P', {OUT=>"9.0Z"}], - - # Numbers>999Y are never acceptable, regardless of scaling - ['large-14','--from=si --to=si 999Y', {OUT=>"999Y"}], - ['large-14.1','--from=si --to=si 1000Y', - {ERR => "$prog: value too large to be printed: '1e+27' " . - "(cannot handle values > 999Y)\n"}, - {EXIT => 2}], - ['large-14.2','--from=si --to=si --from-unit=10000 1Y', - {ERR => "$prog: value too large to be printed: '1e+28' " . - "(cannot handle values > 999Y)\n"}, - {EXIT => 2}], - - # intmax_t overflow when rounding caused this to fail before 8.24 - ['large-15',$limits->{INTMAX_OFLOW}, {OUT=>$limits->{INTMAX_OFLOW}}], - ['large-16','9.300000000000000000', {OUT=>'9.300000000000000000'}], - # Leading zeros weren't handled appropriately before 8.24 ['leading-1','0000000000000000000000000001', {OUT=>"1"}], ['leading-2','.1', {OUT=>"0.1"}], @@ -696,14 +546,6 @@ my @Tests = ['debug-1.1', '--debug --padding 10 4096', {OUT=>" 4096"}], ['debug-2', '--debug --grouping --from=si 4.0K', {OUT=>"4000"}, {ERR=>"$prog: grouping has no effect in this locale\n"}], - ['debug-4', '--to=si --debug 12345678901234567890', - {OUT=>"13E"}, - {ERR=>"$prog: large input value '12345678901234567890':" . - " possible precision loss\n"}], - ['debug-5', '--to=si --from=si --debug 1.12345678901234567890Y', - {OUT=>"1.2Y"}, - {ERR=>"$prog: large input value '1.12345678901234567890Y':" . - " possible precision loss\n"}], # dev-debug messages - the actual messages don't matter # just ensure the program works, and for code coverage testing. @@ -765,8 +607,8 @@ my @Tests = ['fmt-err-6', '--format "%f %f"', {ERR=>"$prog: format '%f %f' has too many % directives\n"}, {EXIT=>1}], - ['fmt-err-7', '--format "%123456789012345678901234567890f"', - {ERR=>"$prog: invalid format '%123456789012345678901234567890f'". + ['fmt-err-7', '--format "%'.$limits->{LONG_OFLOW}.'f"', + {ERR=>"$prog: invalid format '%$limits->{LONG_OFLOW}f'". " (width overflow)\n"}, {EXIT=>1}], ['fmt-err-9', '--format "%f" --grouping', @@ -856,16 +698,6 @@ my @Tests = "'12M' (e.g Ki/Mi/Gi)\n"}, {OUT => "12M\n"}, {EXIT=>2}], - ['ign-err-10','--invalid=fail 10000000000000000000', - {ERR => "$prog: value too large to be printed: '1e+19' " . - "(consider using --to)\n"}, - {OUT => "10000000000000000000\n"}, - {EXIT=>2}], - ['ign-err-11','--invalid=fail --to=si 9876543210000000000000000000', - {ERR => "$prog: value too large to be converted: " . - "'9876543210000000000000000000'\n"}, - {OUT => "9876543210000000000000000000\n"}, - {EXIT => 2}], ## Ignore Errors with multiple conversions ['ign-err-m1', '--invalid=ignore --to=si 1000 2000 bad 3000', @@ -907,6 +739,182 @@ my @Tests = {EXIT => 2}], ); +my @Limit_Tests = + ( + # Large Values + ['large-1','1000000000000000', {OUT=>"1000000000000000"}], + # 18 digits is OK + ['large-2','1000000000000000000', {OUT=>"1000000000000000000"}], + # 19 digits is too much (without output scaling) + ['large-3','10000000000000000000', + {ERR => "$prog: value too large to be printed: '1e+19' " . + "(consider using --to)\n"}, + {EXIT=>2}], + ['large-4','1000000000000000000.0', + {ERR => "$prog: value/precision too large to be printed: " . + "'1e+18/1' (consider using --to)\n"}, + {EXIT=>2}], + + + # Test input: + # Up to 27 digits is OK. + ['large-3.1', '--to=si 1', {OUT=> "1"}], + ['large-3.2', '--to=si 10', {OUT=> "10"}], + ['large-3.3', '--to=si 100', {OUT=> "100"}], + ['large-3.4', '--to=si 1000', {OUT=>"1.0K"}], + ['large-3.5', '--to=si 10000', {OUT=> "10K"}], + ['large-3.6', '--to=si 100000', {OUT=>"100K"}], + ['large-3.7', '--to=si 1000000', {OUT=>"1.0M"}], + ['large-3.8', '--to=si 10000000', {OUT=> "10M"}], + ['large-3.9', '--to=si 100000000', {OUT=>"100M"}], + ['large-3.10','--to=si 1000000000', {OUT=>"1.0G"}], + ['large-3.11','--to=si 10000000000', {OUT=> "10G"}], + ['large-3.12','--to=si 100000000000', {OUT=>"100G"}], + ['large-3.13','--to=si 1000000000000', {OUT=>"1.0T"}], + ['large-3.14','--to=si 10000000000000', {OUT=> "10T"}], + ['large-3.15','--to=si 100000000000000', {OUT=>"100T"}], + ['large-3.16','--to=si 1000000000000000', {OUT=>"1.0P"}], + ['large-3.17','--to=si 10000000000000000', {OUT=> "10P"}], + ['large-3.18','--to=si 100000000000000000', {OUT=>"100P"}], + ['large-3.19','--to=si 1000000000000000000', {OUT=>"1.0E"}], + ['large-3.20','--to=si 10000000000000000000', {OUT=> "10E"}], + ['large-3.21','--to=si 210000000000000000000', {OUT=>"210E"}], + ['large-3.22','--to=si 3210000000000000000000', {OUT=>"3.3Z"}], + ['large-3.23','--to=si 43210000000000000000000', {OUT=> "44Z"}], + ['large-3.24','--to=si 543210000000000000000000', {OUT=>"544Z"}], + ['large-3.25','--to=si 6543210000000000000000000', {OUT=>"6.6Y"}], + ['large-3.26','--to=si 76543210000000000000000000', {OUT=> "77Y"}], + ['large-3.27','--to=si 876543210000000000000000000', {OUT=>"877Y"}], + + # More than 27 digits is not OK + ['large-3.28','--to=si 9876543210000000000000000000', + {ERR => "$prog: value too large to be converted: " . + "'9876543210000000000000000000'\n"}, + {EXIT => 2}], + + # Test Output + ['large-4.1', '--from=si 9.7M', {OUT=>"9700000"}], + ['large-4.2', '--from=si 10M', {OUT =>"10000000"}], + ['large-4.3', '--from=si 200M', {OUT =>"200000000"}], + ['large-4.4', '--from=si 3G', {OUT =>"3000000000"}], + ['large-4.5', '--from=si 40G', {OUT =>"40000000000"}], + ['large-4.6', '--from=si 500G', {OUT =>"500000000000"}], + ['large-4.7', '--from=si 6T', {OUT =>"6000000000000"}], + ['large-4.8', '--from=si 70T', {OUT =>"70000000000000"}], + ['large-4.9', '--from=si 800T', {OUT =>"800000000000000"}], + ['large-4.10','--from=si 9P', {OUT =>"9000000000000000"}], + ['large-4.11','--from=si 10P', {OUT =>"10000000000000000"}], + ['large-4.12','--from=si 200P', {OUT =>"200000000000000000"}], + ['large-4.13','--from=si 3E', {OUT =>"3000000000000000000"}], + + # More than 18 digits of output without scaling - no good. + ['large-4.14','--from=si 40E', + {ERR => "$prog: value too large to be printed: '4e+19' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.15','--from=si 500E', + {ERR => "$prog: value too large to be printed: '5e+20' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.16','--from=si 6Z', + {ERR => "$prog: value too large to be printed: '6e+21' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.17','--from=si 70Z', + {ERR => "$prog: value too large to be printed: '7e+22' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.18','--from=si 800Z', + {ERR => "$prog: value too large to be printed: '8e+23' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.19','--from=si 9Y', + {ERR => "$prog: value too large to be printed: '9e+24' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.20','--from=si 10Y', + {ERR => "$prog: value too large to be printed: '1e+25' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.21','--from=si 200Y', + {ERR => "$prog: value too large to be printed: '2e+26' " . + "(consider using --to)\n"}, + {EXIT => 2}], + + ['large-5.1','--to=si 1000000000000000000', {OUT=>"1.0E"}], + ['large-5','--from=si --to=si 2E', {OUT=>"2.0E"}], + ['large-6','--from=si --to=si 3.4Z', {OUT=>"3.4Z"}], + ['large-7','--from=si --to=si 80Y', {OUT=>"80Y"}], + ['large-8','--from=si --to=si 9000Z', {OUT=>"9.0Y"}], + + ['large-10','--from=si --to=si 999Y', {OUT=>"999Y"}], + ['large-11','--from=si --to=iec 999Y', {OUT=>"827Y"}], + ['large-12','--from=si --round=down --to=iec 999Y', {OUT=>"826Y"}], + + # units can also affect the output + ['large-13','--from=si --from-unit=1000000 9P', + {ERR => "$prog: value too large to be printed: '9e+21' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-13.1','--from=si --from-unit=1000000 --to=si 9P', {OUT=>"9.0Z"}], + + # Numbers>999Y are never acceptable, regardless of scaling + ['large-14','--from=si --to=si 999Y', {OUT=>"999Y"}], + ['large-14.1','--from=si --to=si 1000Y', + {ERR => "$prog: value too large to be printed: '1e+27' " . + "(cannot handle values > 999Y)\n"}, + {EXIT => 2}], + ['large-14.2','--from=si --to=si --from-unit=10000 1Y', + {ERR => "$prog: value too large to be printed: '1e+28' " . + "(cannot handle values > 999Y)\n"}, + {EXIT => 2}], + + # intmax_t overflow when rounding caused this to fail before 8.24 + ['large-15',$limits->{INTMAX_OFLOW}, {OUT=>$limits->{INTMAX_OFLOW}}], + ['large-16','9.300000000000000000', {OUT=>'9.300000000000000000'}], + + # INTEGRAL_OVERFLOW + ['strtod-3', '--from=si "1234567890123456789012345678901234567890'. + '1234567890123456789012345678901234567890"', + {ERR=>"$prog: value too large to be converted: '" . + "1234567890123456789012345678901234567890" . + "1234567890123456789012345678901234567890'\n", + }, + {EXIT=> 2}], + + # FRACTION_OVERFLOW + ['strtod-7', '--from=si "12.1234567890123456789012345678901234567890'. + '1234567890123456789012345678901234567890"', + {ERR=>"$prog: value too large to be converted: '" . + "12.1234567890123456789012345678901234567890" . + "1234567890123456789012345678901234567890'\n", + }, + {EXIT=> 2}], + + ['debug-4', '--to=si --debug 12345678901234567890', + {OUT=>"13E"}, + {ERR=>"$prog: large input value '12345678901234567890':" . + " possible precision loss\n"}], + ['debug-5', '--to=si --from=si --debug 1.12345678901234567890Y', + {OUT=>"1.2Y"}, + {ERR=>"$prog: large input value '1.12345678901234567890Y':" . + " possible precision loss\n"}], + + ['ign-err-10','--invalid=fail 10000000000000000000', + {ERR => "$prog: value too large to be printed: '1e+19' " . + "(consider using --to)\n"}, + {OUT => "10000000000000000000\n"}, + {EXIT=>2}], + ['ign-err-11','--invalid=fail --to=si 9876543210000000000000000000', + {ERR => "$prog: value too large to be converted: " . + "'9876543210000000000000000000'\n"}, + {OUT => "9876543210000000000000000000\n"}, + {EXIT => 2}], + ); +# Restrict these tests to systems with LDBL_DIG == 18 +(system "$prog ---debug 1 2>&1|grep 'MAX_UNSCALED_DIGITS: 18' > /dev/null") == 0 + and push @Tests, @Limit_Tests; + my @Locale_Tests = ( # Locale that supports grouping, but without '--grouping' parameter -- 2.4.1