From: SADAHIRO Tomoyuki Date: Sun, 15 Oct 2006 16:51:34 +0000 (+0900) Subject: Re: sprintf 64 test X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=9911cee9a9c011ce0c7f2203e6247489dafc24ae;p=p5sagit%2Fp5-mst-13.2.git Re: sprintf 64 test 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 --- diff --git a/pod/perlfunc.pod b/pod/perlfunc.pod index af5fe70..cda8f56 100644 --- a/pod/perlfunc.pod +++ b/pod/perlfunc.pod @@ -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 "" + printf '<%.*s>', 3, "string"; # prints "" + printf '<%.*s>', 0, "string"; # prints "<>" + printf '<%.*s>', -1, "string"; # prints "" + + 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 --- 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; diff --git a/t/op/sprintf.t b/t/op/sprintf.t index 269489f..11eeee4 100755 --- a/t/op/sprintf.t +++ b/t/op/sprintf.t @@ -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< diff --git a/t/op/sprintf2.t b/t/op/sprintf2.t index 90214ab..c92ab89 100644 --- a/t/op/sprintf2.t +++ b/t/op/sprintf2.t @@ -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)/); + } + } + } + } +} +