tags:

views:

156

answers:

3

I'm trying to debug a problem on a remote user's site. We've narrowed it down to a problem with formatted output in Perl. The user swears up and down that

perl -e 'printf "Number: %lG\n", 0.1'

prints

Number: %lG

not

Number: 0.1

The user reports that their Perl is version 5.8. The oldest version I have around is 5.8.1, and it seems to behave correctly.

Any guesses? Misconfiguration? Module conflicts?

+4  A: 

It should only do that if it doesn't recognise the format specifier. For example:

pax> perl -e 'printf "Number: %q\n", 0.1'
Number: %q

I think you're going to have to go on site to figure this one out although you may want to first get them to cut and paste the text and a screen dump from, for example, HyperSnap demo, into an email so you can check it carefully. I only suggest that one since we use it internally and it has a free trial. You can use any decent screen capture program you like.

I originally thought that they may be typing in 1G (wun jee) instead of lG (ell jee) but the former still works.

I do notice the IG (eye jee) will print out the text rather than the number but, unless they're using a particularly bad font, that should be a recognisable difference.

BTW, I only have 5.10 to work with so it may be a version problem. It's hard to imagine, however, that there'd be a major difference between 5.8 and 5.8.1.

paxdiablo
it works in 5.6, I think it is fair to call this user error.
MkV
@MkV, thanks for checking that in 5.6. It's very helpful to know that.
Charles E. Grant
+7  A: 

Quoting from the sprintf documentation:

Returns a string formatted by the usual printf conventions of the C library function sprintf. See below for more details and see sprintf(3) or printf(3) on your system for an explanation of the general principles.

IOW, like with many built-ins, Perl just thinly wraps a function, and it's platform dependent.

Perl's sprintf permits the following universally-known conversions:

%l is not part of it. My guess is that the remote user is not using GNU. He can find out exactly what is supported by his unwashed Unix by typing man 3 sprintf or man 3 printf.

daxim
That makes sense. I'll be glad when all the non-GNU systems disappear :-) +1.
paxdiablo
Also quoting from sprintf documentation, should have read a little closer daxim: Perl does its own "sprintf" formatting -it emulates the C function "sprintf", but it doesn't use it (except for floating-point numbers, and even then only the standard modifiers are allowed). As a result, any non-standard extensions in your local "sprintf" are not available from Perl.
MkV
%lG is supported by sprintf: %G like %g, but with an upper-case "E" (if applicable); and (under size): 'l interpret integer as C type "long" or "unsigned long"
MkV
%lG is a valid format in POSIX/Single Unix Specification. Just because GNU sometimes copies from POSIX correctly doesn't make it better.
MkV
Actually it looks like %lG is not a valid specifier. %ld is ok, or %llG, but not %lG. See my answer for details. Daxim's answer is pretty close to the actual problem. Not a GNU/Posix issue though, just an incorrect specifier.
Charles E. Grant
+2  A: 
It looks like it was a Perl build configuration issue. The Perl build supports a `d_longdbl` option, which indicates whether long doubles are allowed or not. You can test whether it is set on your machine with:
perl -V:d_longdbl

More info at perldoc sprintf. Thanks for your input everybody.


Edit:

Nope, that wasn't it either. Close inspection of the sprintf documentation revealed that the modifiers for a long double are q, ll, and L, NOT l. l is a valid modifer for integer types. D'oh.

It looks like most installations of perl will silently ignore the l, and parse the rest of the modifier correctly. Except on our user's site. ☹ Anyway, the problem was fixed by using a valid modifier for a long double.

FYI, I played with the same format specifiers in the C printf.

printf("The number is %lG\n", 0.001);
printf("The number is %LG\n", 0.001);

The first call “worked”, printing out 0.001, but the second call printed out a garbage value until I properly specified the type of the numeric literal:

printf("The number is %LG\n", 0.001L);

Apparently the C printf is silently ignoring the improper l modifier. This makes me suspect that most Perl installations ignore it too.

Charles E. Grant
Your answer is the best fit for the question. Go ahead an accept it instead of mine so that it is placed at the top where it belongs.
daxim