# HG changeset patch # User Christian Brabandt # Date 1472500807 -7200 # Node ID 7cf4e210cf3c105523b01feaf9245601ca4edc70 # Parent f359ee3f123e16934be2101d3c6114e665ee2561 commit https://github.com/vim/vim/commit/04186095346daa60e82e981dad114de2b641d672 Author: Bram Moolenaar Date: Mon Aug 29 21:55:35 2016 +0200 patch 7.4.2291 Problem: printf() handles floats wrong when there is a sign. Solution: Fix placing the sign. Add tests. (Dominique Pelle) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Aug 28 +*eval.txt* For Vim version 7.4. Last change: 2016 Aug 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -643,7 +643,7 @@ 2. Expression syntax *expression-syn Expression syntax summary, from least to most significant: -|expr1| expr2 ? expr1 : expr1 if-then-else +|expr1| expr2 ? expr1 : expr1 if-then-else |expr2| expr3 || expr3 .. logical OR @@ -736,7 +736,9 @@ use in a variable such as "a:1". expr2 and expr3 *expr2* *expr3* --------------- - *expr-barbar* *expr-&&* +expr3 || expr3 .. logical OR *expr-barbar* +expr4 && expr4 .. logical AND *expr-&&* + The "||" and "&&" operators take one argument on each side. The arguments are (converted to) Numbers. The result is: @@ -1981,7 +1983,7 @@ assert_notmatch({pat}, {text} [, {msg}]) assert_true({actual} [, {msg}]) none assert {actual} is true asin({expr}) Float arc sine of {expr} atan({expr}) Float arc tangent of {expr} -atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2} +atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2} browse({save}, {title}, {initdir}, {default}) String put up a file requester browsedir({title}, {initdir}) String put up a directory requester @@ -5905,9 +5907,10 @@ printf({fmt}, {expr1} ...) *printf()* %X hex number using upper case letters %o octal number %08b binary number padded with zeros to at least 8 chars - %f floating point number in the form 123.456 - %e floating point number in the form 1.234e3 - %E floating point number in the form 1.234E3 + %f floating point number as 12.23, inf, -inf or nan + %F floating point number as 12.23, INF, -INF or NAN + %e floating point number as 1.23e3, inf, -inf or nan + %E floating point number as 1.23E3, INF, -INF or NAN %g floating point number, as %f or %e depending on value %G floating point number, as %f or %E depending on value %% the % character itself @@ -6039,8 +6042,9 @@ printf({fmt}, {expr1} ...) *printf()* digits after the decimal point. When the precision is zero the decimal point is omitted. When the precision is not specified 6 is used. A really big number - (out of range or dividing by zero) results in "inf". - "0.0 / 0.0" results in "nan". + (out of range or dividing by zero) results in "inf" + or "-inf" with %f (INF or -INF with %F). + "0.0 / 0.0" results in "nan" with %f (NAN with %F). Example: > echo printf("%.2f", 12.115) < 12.12 @@ -7507,7 +7511,7 @@ system({expr} [, {input}]) *system()* Pipes are not used, the 'shelltemp' option is not used. - When prepended by |:silent| the shell will not be set to + When prepended by |:silent| the terminal will not be set to cooked mode. This is meant to be used for commands that do not need the user to type. It avoids stray characters showing up on the screen which require |CTRL-L| to remove. > diff --git a/src/message.c b/src/message.c --- a/src/message.c +++ b/src/message.c @@ -4030,7 +4030,7 @@ infinity_str(int positive, * with flags: '-', '+', ' ', '0' and '#'. * An asterisk is supported for field width as well as precision. * - * Limited support for floating point was added: 'f', 'e', 'E', 'g', 'G'. + * Limited support for floating point was added: 'f', 'F', 'e', 'E', 'g', 'G'. * * Length modifiers 'h' (short int) and 'l' (long int) and 'll' (long long int) * are supported. @@ -4286,7 +4286,6 @@ vim_vsnprintf( case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; - case 'F': fmt_spec = 'f'; break; default: break; } @@ -4715,6 +4714,7 @@ vim_vsnprintf( # ifdef FEAT_FLOAT case 'f': + case 'F': case 'e': case 'E': case 'g': @@ -4740,13 +4740,13 @@ vim_vsnprintf( * "1.0" as "1", we don't want that. */ if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) - fmt_spec = 'f'; + fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; else fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; remove_trailing_zeroes = TRUE; } - if (fmt_spec == 'f' && + if ((fmt_spec == 'f' || fmt_spec == 'F') && # ifdef VAX abs_f > 1.0e38 # else @@ -4762,23 +4762,6 @@ vim_vsnprintf( } else { - format[0] = '%'; - l = 1; - if (precision_specified) - { - size_t max_prec = TMP_LEN - 10; - - /* Make sure we don't get more digits than we - * have room for. */ - if (fmt_spec == 'f' && abs_f > 1.0) - max_prec -= (size_t)log10(abs_f); - if (precision > max_prec) - precision = max_prec; - l += sprintf(format + 1, ".%d", (int)precision); - } - format[l] = fmt_spec; - format[l + 1] = NUL; - if (isnan(f)) { /* Not a number: nan or NAN */ @@ -4795,8 +4778,30 @@ vim_vsnprintf( zero_padding = 0; } else + { /* Regular float number */ + format[0] = '%'; + l = 1; + if (force_sign) + format[l++] = space_for_positive ? ' ' : '+'; + if (precision_specified) + { + size_t max_prec = TMP_LEN - 10; + + /* Make sure we don't get more digits than we + * have room for. */ + if ((fmt_spec == 'f' || fmt_spec == 'F') + && abs_f > 1.0) + max_prec -= (size_t)log10(abs_f); + if (precision > max_prec) + precision = max_prec; + l += sprintf(format + l, ".%d", (int)precision); + } + format[l] = fmt_spec; + format[l + 1] = NUL; + str_arg_l = sprintf(tmp, format, f); + } if (remove_trailing_zeroes) { @@ -4804,7 +4809,7 @@ vim_vsnprintf( char *tp; /* Using %g or %G: remove superfluous zeroes. */ - if (fmt_spec == 'f') + if (fmt_spec == 'f' || fmt_spec == 'F') tp = tmp + str_arg_l - 1; else { @@ -4861,6 +4866,13 @@ vim_vsnprintf( } } } + if (zero_padding && min_field_width > str_arg_l + && (tmp[0] == '-' || force_sign)) + { + /* padding 0's should be inserted after the sign */ + number_of_zeros_to_pad = min_field_width - str_arg_l; + zero_padding_insertion_ind = 1; + } str_arg = tmp; break; } diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -162,21 +162,44 @@ function Test_printf_misc() call assert_equal(' +123', printf('%+6d', 123)) call assert_equal(' 123', printf('% 6d', 123)) call assert_equal(' -123', printf('% 6d', -123)) + + " Test left adjusted. + call assert_equal('123 ', printf('%-6d', 123)) call assert_equal('+123 ', printf('%-+6d', 123)) call assert_equal(' 123 ', printf('%- 6d', 123)) call assert_equal('-123 ', printf('%- 6d', -123)) + call assert_equal(' 00123', printf('%7.5d', 123)) + call assert_equal(' -00123', printf('%7.5d', -123)) + call assert_equal(' +00123', printf('%+7.5d', 123)) + " Precision field should not be used when combined with %0 + call assert_equal(' 00123', printf('%07.5d', 123)) + call assert_equal(' -00123', printf('%07.5d', -123)) + + call assert_equal(' 123', printf('%*d', 5, 123)) + call assert_equal('123 ', printf('%*d', -5, 123)) call assert_equal('00123', printf('%.*d', 5, 123)) call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) - call assert_equal('123 ', printf('%-5d', 123)) + " Simple quote (thousand grouping char) is ignored. + call assert_equal('+00123456', printf("%+'09d", 123456)) + + " Unrecognized format specifier kept as-is. + call assert_equal('_123', printf("%_%d", 123)) + + " Test alternate forms. call assert_equal('0x7b', printf('%#x', 123)) call assert_equal('0X7B', printf('%#X', 123)) call assert_equal('0173', printf('%#o', 123)) call assert_equal('0173', printf('%#O', 123)) call assert_equal('abc', printf('%#s', 'abc')) call assert_equal('abc', printf('%#S', 'abc')) + call assert_equal(' 0173', printf('%#6o', 123)) + call assert_equal(' 00173', printf('%#6.5o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal('0173', printf('%#2.2o', 123)) call assert_equal(' 00123', printf('%6.5d', 123)) call assert_equal(' 0007b', printf('%6.5x', 123)) @@ -201,6 +224,7 @@ endfunc function Test_printf_float() if has('float') + call assert_equal('1.000000', printf('%f', 1)) call assert_equal('1.230000', printf('%f', 1.23)) call assert_equal('1.230000', printf('%F', 1.23)) call assert_equal('9999999.9', printf('%g', 9999999.9)) @@ -215,10 +239,31 @@ function Test_printf_float() call assert_equal(' 0.33', printf('%6.2f', 1.0/3.0)) call assert_equal(' -0.33', printf('%6.2f', -1.0/3.0)) call assert_equal('000.33', printf('%06.2f', 1.0/3.0)) - " FIXME: call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) - " FIXME: call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) - " FIXME: call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) - " FIXME: call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) + call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) + call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) + call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('000.33', printf('%06.2g', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2g', -1.0/3.0)) + call assert_equal('0.33', printf('%3.2f', 1.0/3.0)) + call assert_equal('003.33e-01', printf('%010.2e', 1.0/3.0)) + call assert_equal(' 03.33e-01', printf('% 010.2e', 1.0/3.0)) + call assert_equal('+03.33e-01', printf('%+010.2e', 1.0/3.0)) + call assert_equal('-03.33e-01', printf('%010.2e', -1.0/3.0)) + + " When precision is 0, the dot should be omitted. + call assert_equal(' 2', printf('%3.f', 7.0/3.0)) + call assert_equal(' 2', printf('%3.g', 7.0/3.0)) + call assert_equal(' 2e+00', printf('%7.e', 7.0/3.0)) + + " Float zero can be signed. + call assert_equal('+0.000000', printf('%+f', 0.0)) + call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) + call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) " Float infinity can be signed. call assert_equal('inf', printf('%f', 1.0/0.0)) @@ -227,6 +272,8 @@ function Test_printf_float() call assert_equal('-inf', printf('%g', -1.0/0.0)) call assert_equal('inf', printf('%e', 1.0/0.0)) call assert_equal('-inf', printf('%e', -1.0/0.0)) + call assert_equal('INF', printf('%F', 1.0/0.0)) + call assert_equal('-INF', printf('%F', -1.0/0.0)) call assert_equal('INF', printf('%E', 1.0/0.0)) call assert_equal('-INF', printf('%E', -1.0/0.0)) call assert_equal('INF', printf('%E', 1.0/0.0)) @@ -245,6 +292,9 @@ function Test_printf_float() call assert_equal('-inf ', printf('%-6f', -1.0/0.0)) call assert_equal('+inf ', printf('%-+6f', 1.0/0.0)) call assert_equal(' inf ', printf('%- 6f', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6F', -1.0/0.0)) + call assert_equal('+INF ', printf('%-+6F', 1.0/0.0)) + call assert_equal(' INF ', printf('%- 6F', 1.0/0.0)) call assert_equal('INF ', printf('%-6G', 1.0/0.0)) call assert_equal('-INF ', printf('%-6G', -1.0/0.0)) call assert_equal('INF ', printf('%-6E', 1.0/0.0)) @@ -252,22 +302,16 @@ function Test_printf_float() call assert_equal('inf', printf('%s', 1.0/0.0)) call assert_equal('-inf', printf('%s', -1.0/0.0)) - " Float zero can be signed. - call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) - call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) - call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) - call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) - call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) - call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) - " Float nan (not a number) has no sign. call assert_equal('nan', printf('%f', sqrt(-1.0))) call assert_equal('nan', printf('%f', 0.0/0.0)) call assert_equal('nan', printf('%f', -0.0/0.0)) call assert_equal('nan', printf('%g', 0.0/0.0)) call assert_equal('nan', printf('%e', 0.0/0.0)) + call assert_equal('NAN', printf('%F', 0.0/0.0)) call assert_equal('NAN', printf('%G', 0.0/0.0)) call assert_equal('NAN', printf('%E', 0.0/0.0)) + call assert_equal('NAN', printf('%F', -0.0/0.0)) call assert_equal('NAN', printf('%G', -0.0/0.0)) call assert_equal('NAN', printf('%E', -0.0/0.0)) call assert_equal(' nan', printf('%6f', 0.0/0.0)) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2291, +/**/ 2290, /**/ 2289,