vim-patch:7.4.2291

Problem:    printf() handles floats wrong when there is a sign.
Solution:   Fix placing the sign.  Add tests. (Dominique Pelle)

0418609534
This commit is contained in:
Jurica Bradaric
2017-03-07 21:05:02 +01:00
committed by Jurica Bradaric
parent 6ca580be9b
commit cad9a76be2
4 changed files with 97 additions and 42 deletions

View File

@@ -5683,9 +5683,10 @@ printf({fmt}, {expr1} ...) *printf()*
%04x hex number padded with zeros to at least 4 characters %04x hex number padded with zeros to at least 4 characters
%X hex number using upper case letters %X hex number using upper case letters
%o octal number %o octal number
%f floating point number in the form 123.456 %f floating point number as 12.23, inf, -inf or nan
%e floating point number in the form 1.234e3 %F floating point number as 12.23, INF, -INF or NAN
%E floating point number in the form 1.234E3 %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
%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 %% the % character itself
@@ -5810,8 +5811,9 @@ printf({fmt}, {expr1} ...) *printf()*
digits after the decimal point. When the precision is digits after the decimal point. When the precision is
zero the decimal point is omitted. When the precision zero the decimal point is omitted. When the precision
is not specified 6 is used. A really big number is not specified 6 is used. A really big number
(out of range or dividing by zero) results in "inf". (out of range or dividing by zero) results in "inf"
"0.0 / 0.0" results in "nan". or "-inf" with %f (INF or -INF with %F).
"0.0 / 0.0" results in "nan" with %f (NAN with %F).
Example: > Example: >
echo printf("%.2f", 12.115) echo printf("%.2f", 12.115)
< 12.12 < 12.12

View File

@@ -925,7 +925,6 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
case 'F': fmt_spec = 'f'; break;
default: break; default: break;
} }
@@ -1202,6 +1201,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
} }
case 'f': case 'f':
case 'F':
case 'e': case 'e':
case 'E': case 'E':
case 'g': case 'g':
@@ -1217,37 +1217,19 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
if (fmt_spec == 'g' || fmt_spec == 'G') { if (fmt_spec == 'g' || fmt_spec == 'G') {
// can't use %g directly, cause it prints "1.0" as "1" // can't use %g directly, cause it prints "1.0" as "1"
if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { 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 { } else {
fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
} }
remove_trailing_zeroes = true; remove_trailing_zeroes = true;
} }
if (fmt_spec == 'f' && abs_f > 1.0e307) { if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0e307) {
STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
force_sign, space_for_positive)); force_sign, space_for_positive));
str_arg_l = STRLEN(tmp); str_arg_l = STRLEN(tmp);
zero_padding = 0; zero_padding = 0;
} else { } else {
format[0] = '%';
int 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 += snprintf(format + 1, sizeof(format) - 1, ".%d",
(int)precision);
}
format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec);
format[l + 1] = NUL;
if (isnan(f)) { if (isnan(f)) {
// Not a number: nan or NAN // Not a number: nan or NAN
STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan");
@@ -1259,6 +1241,27 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
str_arg_l = STRLEN(tmp); str_arg_l = STRLEN(tmp);
zero_padding = 0; zero_padding = 0;
} else { } else {
format[0] = '%';
int 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 += snprintf(format + l, sizeof(format) - 1, ".%d",
(int)precision);
}
format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec);
format[l + 1] = NUL;
// Regular float number // Regular float number
assert(l + 1 < (int)sizeof(format)); assert(l + 1 < (int)sizeof(format));
str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f);
@@ -1270,7 +1273,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
char *tp; char *tp;
// using %g or %G: remove superfluous zeroes // using %g or %G: remove superfluous zeroes
if (fmt_spec == 'f') { if (fmt_spec == 'f' || fmt_spec == 'F') {
tp = tmp + str_arg_l - 1; tp = tmp + str_arg_l - 1;
} else { } else {
tp = (char *)vim_strchr((char_u *)tmp, tp = (char *)vim_strchr((char_u *)tmp,
@@ -1312,6 +1315,12 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
} }
} }
} }
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; str_arg = tmp;
break; break;
} }

View File

@@ -141,7 +141,7 @@ function Test_printf_misc()
call assert_equal('173', printf('%O', 123)) call assert_equal('173', printf('%O', 123))
call assert_equal('7b', printf('%x', 123)) call assert_equal('7b', printf('%x', 123))
call assert_equal('7B', printf('%X', 123)) call assert_equal('7B', printf('%X', 123))
call assert_equal('#', printf('%c', 123)) call assert_equal('{', printf('%c', 123))
call assert_equal('abc', printf('%s', 'abc')) call assert_equal('abc', printf('%s', 'abc'))
call assert_equal('abc', printf('%S', 'abc')) call assert_equal('abc', printf('%S', 'abc'))
@@ -160,21 +160,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)) 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(' 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('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('%+ *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('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('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('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(' 00123', printf('%6.5d', 123))
call assert_equal(' 0007b', printf('%6.5x', 123)) call assert_equal(' 0007b', printf('%6.5x', 123))
@@ -198,6 +221,7 @@ function Test_printf_misc()
endfunc endfunc
function Test_printf_float() function Test_printf_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('1.230000', printf('%F', 1.23)) call assert_equal('1.230000', printf('%F', 1.23))
call assert_equal('9999999.9', printf('%g', 9999999.9)) call assert_equal('9999999.9', printf('%g', 9999999.9))
@@ -212,10 +236,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(' -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)) 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)) 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))
" 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))
" 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('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. " Float infinity can be signed.
call assert_equal('inf', printf('%f', 1.0/0.0)) call assert_equal('inf', printf('%f', 1.0/0.0))
@@ -224,6 +269,8 @@ function Test_printf_float()
call assert_equal('-inf', printf('%g', -1.0/0.0)) 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('%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)) 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))
@@ -242,6 +289,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('%-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('%-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)) call assert_equal('INF ', printf('%-6E', 1.0/0.0))
@@ -249,22 +299,16 @@ function Test_printf_float()
call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) call assert_equal("str2float('inf')", printf('%s', 1.0/0.0))
call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) call assert_equal("-str2float('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. " Float nan (not a number) has no sign.
call assert_equal('nan', printf('%f', sqrt(-1.0))) 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('%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('%g', 0.0/0.0))
call assert_equal('nan', printf('%e', 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('%G', 0.0/0.0))
call assert_equal('NAN', printf('%E', 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('%G', -0.0/0.0))
call assert_equal('NAN', printf('%E', -0.0/0.0)) call assert_equal('NAN', printf('%E', -0.0/0.0))
call assert_equal(' nan', printf('%6f', 0.0/0.0)) call assert_equal(' nan', printf('%6f', 0.0/0.0))

View File

@@ -150,7 +150,7 @@ static int included_patches[] = {
2294, 2294,
// 2293, // 2293,
2292, 2292,
// 2291, 2291,
// 2290 NA // 2290 NA
// 2289 NA // 2289 NA
// 2288 NA // 2288 NA