Re: sprintf 64 test
SADAHIRO Tomoyuki [Sun, 15 Oct 2006 16:51:34 +0000 (01:51 +0900)]
Message-Id: <20061015165052.77AD.BQW10602@nifty.com>

1. nullify 0 flag in integer conversions when precision is given
2. ignore space after a plus sign as a sign for a nonnegative number
3. make a negative precision through * working as if the precision
   is omitted

p4raw-id: //depot/perl@29025

pod/perlfunc.pod
sv.c
t/op/sprintf.t
t/op/sprintf2.t

index af5fe70..cda8f56 100644 (file)
@@ -5569,6 +5569,12 @@ For example:
   printf '<%06s>', 12;  # prints "<000012>"
   printf '<%#x>', 12;   # prints "<0xc>"
 
+When a space and a plus sign are given as the flags at once,
+a plus sign is used to prefix a positive number.
+
+  printf '<%+ d>', 12;   # prints "<+12>"
+  printf '<% +d>', 12;   # prints "<+12>"
+
 =item vector flag
 
 This flag tells perl to interpret the supplied string as a vector of
@@ -5635,11 +5641,22 @@ including prior to the decimal point as well as after it, e.g.:
   printf '<%.4g>', 100.01; # prints "<100>"
 
 For integer conversions, specifying a precision implies that the
-output of the number itself should be zero-padded to this width:
+output of the number itself should be zero-padded to this width,
+where the 0 flag is ignored:
+
+  printf '<%.6d>', 1;      # prints "<000001>"
+  printf '<%+.6d>', 1;     # prints "<+000001>"
+  printf '<%-10.6d>', 1;   # prints "<000001    >"
+  printf '<%10.6d>', 1;    # prints "<    000001>"
+  printf '<%010.6d>', 1;   # prints "<    000001>"
+  printf '<%+10.6d>', 1;   # prints "<   +000001>"
 
   printf '<%.6x>', 1;      # prints "<000001>"
   printf '<%#.6x>', 1;     # prints "<0x000001>"
   printf '<%-10.6x>', 1;   # prints "<000001    >"
+  printf '<%10.6x>', 1;    # prints "<    000001>"
+  printf '<%010.6x>', 1;   # prints "<    000001>"
+  printf '<%#10.6x>', 1;   # prints "<  0x000001>"
 
 For string conversions, specifying a precision truncates the string
 to fit in the specified width:
@@ -5652,6 +5669,18 @@ You can also get the precision from the next argument using C<.*>:
   printf '<%.6x>', 1;       # prints "<000001>"
   printf '<%.*x>', 6, 1;    # prints "<000001>"
 
+If a precision obtained through C<*> is negative, it has the same
+effect as no precision.
+
+  printf '<%.*s>',  7, "string";   # prints "<string>"
+  printf '<%.*s>',  3, "string";   # prints "<str>"
+  printf '<%.*s>',  0, "string";   # prints "<>"
+  printf '<%.*s>', -1, "string";   # prints "<string>"
+
+  printf '<%.*d>',  1, 0;   # prints "<0>"
+  printf '<%.*d>',  0, 0;   # prints "<>"
+  printf '<%.*d>', -1, 0;   # prints "<0>"
+
 You cannot currently get the precision from a specified number,
 but it is intended that this will be possible in the future using
 e.g. C<.*2$>:
diff --git a/sv.c b/sv.c
index 7dd83cc..52fa64f 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -8659,7 +8659,10 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
            switch (*q) {
            case ' ':
            case '+':
-               plus = *q++;
+               if (plus == '+' && *q == ' ') /* '+' over ' ' */
+                   q++;
+               else
+                   plus = *q++;
                continue;
 
            case '-':
@@ -8796,14 +8799,15 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                else
                    i = (ewix ? ewix <= svmax : svix < svmax)
                        ? SvIVx(svargs[ewix ? ewix-1 : svix++]) : 0;
-               precis = (i < 0) ? 0 : i;
+               precis = i;
+               has_precis = !(i < 0);
            }
            else {
                precis = 0;
                while (isDIGIT(*q))
                    precis = precis * 10 + (*q++ - '0');
+               has_precis = TRUE;
            }
-           has_precis = TRUE;
        }
 
        /* SIZE */
@@ -9136,6 +9140,10 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                        zeros = precis - elen;
                    else if (precis == 0 && elen == 1 && *eptr == '0')
                        elen = 0;
+
+               /* a precision nullifies the 0 flag. */
+                   if (fill == '0')
+                       fill = ' ';
                }
            }
            break;
index 269489f..11eeee4 100755 (executable)
@@ -210,6 +210,13 @@ __END__
 >%034b<     >2**32-1<     >0011111111111111111111111111111111<
 >%-34b<     >2**32-1<     >11111111111111111111111111111111  <
 >%-034b<    >2**32-1<     >11111111111111111111111111111111  <
+>%6b<       >12<          >  1100<
+>%6.5b<     >12<          > 01100<
+>%-6.5b<    >12<          >01100 <
+>%+6.5b<    >12<          > 01100<
+>% 6.5b<    >12<          > 01100<
+>%06.5b<    >12<          > 01100<         >0 flag with precision: no effect<
+>%.5b<      >12<          >01100<
 >%c<        >ord('A')<    >A<
 >%10c<      >ord('A')<    >         A<
 >%#10c<     >ord('A')<    >         A<     ># modifier: no effect<
@@ -221,20 +228,53 @@ __END__
 >%d<        >123456.789<  >123456<
 >%d<        >-123456.789< >-123456<
 >%d<        >0<           >0<
+>%-d<       >0<           >0<
 >%+d<       >0<           >+0<
+>% d<       >0<           > 0<
 >%0d<       >0<           >0<
+>%-3d<      >1<           >1  <
+>%+3d<      >1<           > +1<
+>% 3d<      >1<           >  1<
+>%03d<      >1<           >001<
+>%+ 3d<     >1<           > +1<
+>% +3d<     >1<           > +1<
 >%.0d<      >0<           ><
 >%+.0d<     >0<           >+<
+>% .0d<     >0<           > <
+>%-.0d<     >0<           ><
+>%#.0d<     >0<           ><
 >%.0d<      >1<           >1<
 >%d<        >1<           >1<
 >%+d<       >1<           >+1<
 >%#3.2d<    >1<           > 01<            ># modifier: no effect<
 >%3.2d<     >1<           > 01<
->%03.2d<    >1<           >001<
+>%03.2d<    >1<           > 01<            >0 flag with precision: no effect<
 >%-3.2d<    >1<           >01 <
+>%+3.2d<    >1<           >+01<
+>% 3.2d<    >1<           > 01<
 >%-03.2d<   >1<           >01 <            >zero pad + left just.: no effect<
+>%3.*d<     >[2,1]<       > 01<
+>%3.*d<     >[1,1]<       >  1<
+>%3.*d<     >[0,1]<       >  1<
+>%3.*d<     >[-1,1]<      >  1<
+>%.*d<      >[0,0]<       ><
+>%-.*d<     >[0,0]<       ><
+>%+.*d<     >[0,0]<       >+<
+>% .*d<     >[0,0]<       > <
+>%0.*d<     >[0,0]<       ><
+>%.*d<      >[-2,0]<      >0<
+>%-.*d<     >[-2,0]<      >0<
+>%+.*d<     >[-2,0]<      >+0<
+>% .*d<     >[-2,0]<      > 0<
+>%0.*d<     >[-2,0]<      >0<
 >%d<        >-1<          >-1<
+>%-d<       >-1<          >-1<
 >%+d<       >-1<          >-1<
+>% d<       >-1<          >-1<
+>%-3d<      >-1<          >-1 <
+>%+3d<      >-1<          > -1<
+>% 3d<      >-1<          > -1<
+>%03d<      >-1<          >-01<
 >%hd<       >1<           >1<              >More extensive testing of<
 >%ld<       >1<           >1<              >length modifiers would be<
 >%Vd<       >1<           >1<              >platform-specific<
@@ -256,14 +296,14 @@ __END__
 >%+-v3d<    >"\01\02\03"< >+1 .2  .3  <
 >%+-v3d<    >[version::qv("1.2.3")]< >+1 .2  .3  <
 >%v4.3d<    >"\01\02\03"< > 001. 002. 003<
->%0v4.3d<   >"\01\02\03"< >0001.0002.0003<
+>%0v4.3d<   >"\01\02\03"< > 001. 002. 003<
 >%0*v2d<    >['-', "\0\7\14"]< >00-07-12<
 >%v.*d<     >["\01\02\03", 3]< >001.002.003<
 >%0v*d<     >["\01\02\03", 3]< >001.002.003<
 >%-v*d<     >["\01\02\03", 3]< >1  .2  .3  <
 >%+-v*d<    >["\01\02\03", 3]< >+1 .2  .3  <
 >%v*.*d<    >["\01\02\03", 4, 3]< > 001. 002. 003<
->%0v*.*d<   >["\01\02\03", 4, 3]< >0001.0002.0003<
+>%0v*.*d<   >["\01\02\03", 4, 3]< > 001. 002. 003<
 >%0*v*d<    >['-', "\0\7\13", 2]< >00-07-11<
 >%0*v*d<    >['-', version::qv("0.7.11"), 2]< >00-07-11<
 >%e<        >1234.875<    >1.234875e+03<
@@ -365,7 +405,44 @@ __END__
 >%#o<       >2**32-1<     >037777777777<
 >%o<        >642<         >1202<          >check smaller octals across platforms<
 >%+o<       >642<         >1202<
+>% o<       >642<         >1202<
 >%#o<       >642<         >01202<
+>%4o<       >18<          >  22<
+>%4.3o<     >18<          > 022<
+>%-4.3o<    >18<          >022 <
+>%+4.3o<    >18<          > 022<
+>% 4.3o<    >18<          > 022<
+>%04.3o<    >18<          > 022<          >0 flag with precision: no effect<
+>%4.o<      >36<          >  44<
+>%-4.o<     >36<          >44  <
+>%+4.o<     >36<          >  44<
+>% 4.o<     >36<          >  44<
+>%04.o<     >36<          >  44<          >0 flag with precision: no effect<
+>%.3o<      >18<          >022<
+>%#4o<      >17<          > 021<
+>%#-4o<     >17<          >021 <
+>%-#4o<     >17<          >021 <
+>%#+4o<     >17<          > 021<
+>%# 4o<     >17<          > 021<
+>%#04o<     >17<          >0021<
+>%#4.o<     >16<          > 020<
+>%#-4.o<    >16<          >020 <
+>%-#4.o<    >16<          >020 <
+>%#+4.o<    >16<          > 020<
+>%# 4.o<    >16<          > 020<
+>%#04.o<    >16<          > 020<          >0 flag with precision: no effect<
+>%#4.3o<    >18<          > 022<
+>%#-4.3o<   >18<          >022 <
+>%-#4.3o<   >18<          >022 <
+>%#+4.3o<   >18<          > 022<
+>%# 4.3o<   >18<          > 022<
+>%#04.3o<   >18<          > 022<          >0 flag with precision: no effect<
+>%#6.4o<    >18<          >  0022<
+>%#-6.4o<   >18<          >0022  <
+>%-#6.4o<   >18<          >0022  <
+>%#+6.4o<   >18<          >  0022<
+>%# 6.4o<   >18<          >  0022<
+>%#06.4o<   >18<          >  0022<        >0 flag with precision: no effect<
 >%d< >$p=sprintf('%p',$p);$p=~/^[0-9a-f]+$/< >1< >Coarse hack: hex from %p?<
 >%d< >$p=sprintf('%-8p',$p);$p=~/^[0-9a-f]+\s*$/< >1< >Coarse hack: hex from %p?<
 >%#p<       >''<          >%#p INVALID<
@@ -381,6 +458,15 @@ __END__
 >%3s<       >'string'<    >string<
 >%.3s<      >'string'<    >str<
 >%.*s<      >[3, 'string']< >str<
+>%.*s<      >[2, 'string']< >st<
+>%.*s<      >[1, 'string']< >s<
+>%.*s<      >[0, 'string']< ><
+>%.*s<      >[-1,'string']< >string<  >negative precision to be ignored<
+>%3.*s<     >[3, 'string']< >str<
+>%3.*s<     >[2, 'string']< > st<
+>%3.*s<     >[1, 'string']< >  s<
+>%3.*s<     >[0, 'string']< >   <
+>%3.*s<     >[-1,'string']< >string<  >negative precision to be ignored<
 >%t<        >''<          >%t INVALID<
 >%u<        >2**32-1<     >4294967295<
 >%+u<       >2**32-1<     >4294967295<
@@ -389,6 +475,13 @@ __END__
 >%012u<     >2**32-1<     >004294967295<
 >%-12u<     >2**32-1<     >4294967295  <
 >%-012u<    >2**32-1<     >4294967295  <
+>%4u<       >18<          >  18<
+>%4.3u<     >18<          > 018<
+>%-4.3u<    >18<          >018 <
+>%+4.3u<    >18<          > 018<
+>% 4.3u<    >18<          > 018<
+>%04.3u<    >18<          > 018<         >0 flag with precision: no effect<
+>%.3u<      >18<          >018<
 >%v<        >''<          >%v INVALID<
 >%w<        >''<          >%w INVALID<
 >%x<        >2**32-1<     >ffffffff<
@@ -399,9 +492,62 @@ __END__
 >%-10x<     >2**32-1<     >ffffffff  <
 >%-010x<    >2**32-1<     >ffffffff  <
 >%0-10x<    >2**32-1<     >ffffffff  <
+>%4x<       >18<          >  12<
+>%4.3x<     >18<          > 012<
+>%-4.3x<    >18<          >012 <
+>%+4.3x<    >18<          > 012<
+>% 4.3x<    >18<          > 012<
+>%04.3x<    >18<          > 012<         >0 flag with precision: no effect<
+>%.3x<      >18<          >012<
+>%4X<       >28<          >  1C<
+>%4.3X<     >28<          > 01C<
+>%-4.3X<    >28<          >01C <
+>%+4.3X<    >28<          > 01C<
+>% 4.3X<    >28<          > 01C<
+>%04.3X<    >28<          > 01C<         >0 flag with precision: no effect<
+>%.3X<      >28<          >01C<
+>%.0x<      >0<           ><
+>%+.0x<     >0<           ><
+>% .0x<     >0<           ><
+>%-.0x<     >0<           ><
+>%#.0x<     >0<           ><
+>%#4x<      >28<          >0x1c<
+>%#4.3x<    >28<          >0x01c<
+>%#-4.3x<   >28<          >0x01c<
+>%#+4.3x<   >28<          >0x01c<
+>%# 4.3x<   >28<          >0x01c<
+>%#04.3x<   >28<          >0x01c<         >0 flag with precision: no effect<
+>%#.3x<     >28<          >0x01c<
+>%#6.3x<    >28<          > 0x01c<
+>%#-6.3x<   >28<          >0x01c <
+>%-#6.3x<   >28<          >0x01c <
+>%#+6.3x<   >28<          > 0x01c<
+>%+#6.3x<   >28<          > 0x01c<
+>%# 6.3x<   >28<          > 0x01c<
+>% #6.3x<   >28<          > 0x01c<
 >%0*x<      >[-10, ,2**32-1]< >ffffffff  <
 >%vx<       >[version::qv("1.2.3")]< >1.2.3<
 >%vx<       >[version::qv("1.20.300")]< >1.14.12c<
+>%.*x<      >[0,0]<       ><
+>%-.*x<     >[0,0]<       ><
+>%+.*x<     >[0,0]<       ><
+>% .*x<     >[0,0]<       ><
+>%0.*x<     >[0,0]<       ><
+>%.*x<      >[-3,0]<      >0<
+>%-.*x<     >[-3,0]<      >0<
+>%+.*x<     >[-3,0]<      >0<
+>% .*x<     >[-3,0]<      >0<
+>%0.*x<     >[-3,0]<      >0<
+>%#.*x<     >[0,0]<       ><
+>%#-.*x<    >[0,0]<       ><
+>%#+.*x<    >[0,0]<       ><
+>%# .*x<    >[0,0]<       ><
+>%#0.*x<    >[0,0]<       ><
+>%#.*x<     >[-1,0]<      >0<
+>%#-.*x<    >[-1,0]<      >0<
+>%#+.*x<    >[-1,0]<      >0<
+>%# .*x<    >[-1,0]<      >0<
+>%#0.*x<    >[-1,0]<      >0<
 >%y<        >''<          >%y INVALID<
 >%z<        >''<          >%z INVALID<
 >%2$d %1$d<    >[12, 34]<      >34 12<
index 90214ab..c92ab89 100644 (file)
@@ -6,7 +6,7 @@ BEGIN {
     require './test.pl';
 }   
 
-plan tests => 284;
+plan tests => 1292;
 
 is(
     sprintf("%.40g ",0.01),
@@ -85,3 +85,52 @@ for (int(~0/2+1), ~0, "9999999999999999999") {
        is ($bad, 0, "pattern '%v' . chr $ord");
     }
 }
+
+sub mysprintf_int_flags {
+    my ($fmt, $num) = @_;
+    die "wrong format $fmt" if $fmt !~ /^%([-+ 0]+)([1-9][0-9]*)d\z/;
+    my $flag  = $1;
+    my $width = $2;
+    my $sign  = $num < 0 ? '-' :
+               $flag =~ /\+/ ? '+' :
+               $flag =~ /\ / ? ' ' :
+               '';
+    my $abs   = abs($num);
+    my $padlen = $width - length($sign.$abs);
+    return
+       $flag =~ /0/ && $flag !~ /-/ # do zero padding
+           ? $sign . '0' x $padlen . $abs
+           : $flag =~ /-/ # left or right
+               ? $sign . $abs . ' ' x $padlen
+               : ' ' x $padlen . $sign . $abs;
+}
+
+# Whole tests for "%4d" with 2 to 4 flags;
+# total counts: 3 * (4**2 + 4**3 + 4**4) == 1008
+
+my @flags = ("-", "+", " ", "0");
+for my $num (0, -1, 1) {
+    for my $f1 (@flags) {
+       for my $f2 (@flags) {
+           for my $f3 ('', @flags) { # '' for doubled flags
+               my $flag = $f1.$f2.$f3;
+               my $width = 4;
+               my $fmt   = '%'."${flag}${width}d";
+               my $result = sprintf($fmt, $num);
+               my $expect = mysprintf_int_flags($fmt, $num);
+               is($result, $expect, qq/sprintf("$fmt",$num)/);
+
+               next if $f3 eq '';
+
+               for my $f4 (@flags) { # quadrupled flags
+                   my $flag = $f1.$f2.$f3.$f4;
+                   my $fmt   = '%'."${flag}${width}d";
+                   my $result = sprintf($fmt, $num);
+                   my $expect = mysprintf_int_flags($fmt, $num);
+                   is($result, $expect, qq/sprintf("$fmt",$num)/);
+               }
+           }
+       }
+    }
+}
+