Return 0 (with a warning) for sprintf("%.0g") and sprintf("%.0f")
Nicholas Clark [Thu, 13 May 2010 11:04:55 +0000 (12:04 +0100)]
There is special case code in the sprintf implementation, for simple %f and %g
formats, conditionally compiled in only when NVs are doubles. Under long
doubles, these are handled by the general purpose code, which always returns
0 if the argument is missing. Note that sprintf(" %.0g"), ie a leading space,
sufficient to bypass the special case code, would return the string " 0".

The special case code used to return an empty string, meaning that the
behaviour of sprintf("%.0g") and sprintf("%.0f") was inconsistent between a
perl built with doubles, and a perl with long doubles, and the behaviour of
sprintf("%.0g") and sprintf(" %.0g") was inconsistent.

5b98cd54dff3b163 fixed #62874 - the special case code did not warn, but
changed behaviour to return 0. d347ad18ecf3da70 undid the behaviour change,
viewing it as a regression. However, the tests added in 5b98cd54dff3b163
expose the inconsistency in behaviour between doubles and long doubles.

There should be no inconsistency, hence the only logically consistent
conclusion is that the special case implementation was wrong - it cannot
give results inconsistent with the general code. Hence this commit changes
it to return 0 (with a warning). This is achieved by simply skipping the
special case code, if there are insufficient arguments.

sv.c
t/op/sprintf.t

diff --git a/sv.c b/sv.c
index 8cbd3a0..db11794 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -9399,15 +9399,8 @@ Perl_sv_vcatpvfn(pTHX_ SV *const sv, const char *const pat, const STRLEN patlen,
        pp = pat + 2;
        while (*pp >= '0' && *pp <= '9')
            digits = 10 * digits + (*pp++ - '0');
-       if (pp - pat == (int)patlen - 1) {
-           NV nv;
-
-           if (svix < svmax)
-               nv = SvNV(*svargs);
-           else {
-               S_vcatpvfn_missing_argument(aTHX);
-               return;
-           }
+       if (pp - pat == (int)patlen - 1 && svix < svmax) {
+           const NV nv = SvNV(*svargs);
            if (*pp == 'g') {
                /* Add check for digits != 0 because it seems that some
                   gconverts are buggy in this case, and we don't yet have
index b153839..14f0395 100644 (file)
@@ -374,7 +374,7 @@ __END__
 >%+8.1f<    >-1234.875<   > -1234.9<
 >%*.*f<     >[5, 2, 12.3456]< >12.35<
 >%f<        >0<           >0.000000<
->%.0f<      >[]<          > MISSING<
+>%.0f<      >[]<          >0 MISSING<
 > %.0f<     >[]<          > 0 MISSING<
 >%.0f<      >0<           >0<
 >%.0f<      >2**38<       >274877906944<   >Should have exact int'l rep'n<
@@ -390,7 +390,7 @@ __END__
 >%g<        >12345.6789<  >12345.7<
 >%+g<       >12345.6789<  >+12345.7<
 >%#g<       >12345.6789<  >12345.7<
->%.0g<      >[]<          > MISSING<
+>%.0g<      >[]<          >0 MISSING<
 > %.0g<     >[]<          > 0 MISSING<
 >%.0g<      >-0.0<        >-0<            >C99 standard mandates minus sign but C89 does not skip: MSWin32 VMS hpux:10.20 openbsd netbsd:1.5 irix darwin<
 >%.0g<      >12345.6789<  >1e+04<