in reply to printf size flag incompatibility

Summary: you're using h and l incorrectly, as they are meant to edit integers. The fact that they worked in 5.6.1 probably was a mistake in the first place, and the fix didn't show up in perldelta. Support for q was probably eliminated a while back and no one remembered to update the docs (though it could also be that the docs represent some changes that were never made).

First, let me show you the results of your code in 5.8.0 with warnings enabled:

3.1416 Invalid conversion in printf: "%f" at - line 3. %4.4lf Invalid conversion in printf: "%f" at - line 4. %4.4hf Invalid conversion in printf: "%q" at - line 5. %4.4qf 3.1416 3.1416
The relevant part of the perldocs follows:
For numeric conversions, you can specify the size to interpret the number as using l, h, V, q, L or ll. For integer conversions, numbers are usually assumed to be whatever the default integer size is on your platform (usually 32 or 64 bits), but you can override this to use instead one of the standard C types, as supported by the compiler used to build Perl:
l interpret integer as C type "long" or "unsigned long" h interpret integer as C type "short" or "unsigned short" q, L or ll interpret integer as C type "long long" or "unsigned lo +ng long" (if your platform supports such a type, else it is an e +rror)
For floating point conversions, numbers are usually assumed to be the default floating point size on your platform (double or long double), but you can force 'long double' with q, L, or ll if your platform supports them.
Source: perldoc -f sprintf, perl 5.8.0

Now, even according to this, the l and h formats only apply to integers and never floats. So, Perl just warns you and then does what it prints what it can make sense of (the literal string). According to these docs, however, q should not be producing an error.

Next source is the best ANSI C description I could find:
The optional character h, specifying that a following d, i, o, u, x, or X conversion corresponds to a short int or unsigned short int argument, or that a following n conversion corresponds to a pointer to a short int argument.

The optional character l (ell) specifying that a following d, i, o, u, x, or X conversion applies to a pointer to a long int or unsigned long int argument, or that a following n conversion corresponds to a pointer to a long int argument.

The character L specifying that a following e, E, f, g, or G conversion corresponds to a long double argument.

Source: http://www.introl.com/introl-demo/Libraries/C/ANSI_C/stdio/fprintf.html

Again, h and l only are for integers, and in this case q and ll don't even exist. These are also the flags that Visual C++ understands, in addition to I64. Of course, Perl is much more than ANSI C, as the previous docs indicated.

So, let's go to the likely inspiration for Perl's s/printf, the GNU C library:

h A following integer conversion corresponds to a short int or unsigned short int argument, or a fol­ lowing n conversion corresponds to a pointer to a short int argument. l (ell) A following integer conversion corresponds to a long int or unsigned long int argument, or a fol­ lowing n conversion corresponds to a pointer to a long int argument, or a following c conversion cor­ responds to a wint_t argument, or a following s conversion corresponds to a pointer to wchar_t argument. ll (ell-ell). A following integer conversion corre­ sponds to a long long int or unsigned long long int argument, or a following n conversion corresponds to a pointer to a long long int argument. L A following a, A, e, E, f, F, g, or G conversion corresponds to a long double argument. (C99 allows %LF, but SUSv2 does not.) q (`quad'. BSD 4.4 and Linux libc5 only. Don't use.) This is a synonym for ll.
Source: http://www.cgi101.com/class/ch16/envman.cgi?sprintf

For one thing, it looks like ll is also an integer only conversion, but Perl lets it slide as a synonym for L. However, q, which is a synonym for ll, doesn't even work on integers. The manpage says we shouldn't use it, it looks like Perl has decided we shouldn't use it, but no one has updated the perldocs to show this.

Two comments: One can argue how Perl should handle incorrect parameters, but I think the current warning is probably sufficient. Also, I'm not quite sure why ll decided to start working on non-ints in 5.8.0, but I still believe it's either the docs being a bit behind or ahead of the times.

Note: in Win32, the tests you performed behave exactly the same, if you're curious.

Replies are listed 'Best First'.
Re: Re: printf size flag incompatibility
by Anonymous Monk on Jul 22, 2002 at 03:33 UTC
    Summary: you're using h and l incorrectly

    Thanks for the info. But to be clear, I was exploring l, h, etc. exploratively and unfortunately only showed a floating point example. The exact same differences (between 5.6.1 and 5.8.0) show up when used for integer conversions.

    # file: printf.pl printf "%4.4f\n", 1; printf "%4.4lf\n", 1; printf "%4.4hf\n", 1; printf "%4.4qf\n", 1; printf "%4.4Lf\n", 1; printf "%4.4llf\n", 1; #outputs: $ /usr/bin/perl printf.pl 1.0000 1.0000 1.0000 %4.4qf %4.4Lf %4.4llf $ /usr/local/bin/perl printf.pl 1.0000 %4.4lf %4.4hf %4.4qf 1.0000 1.0000

    Where /usr/bin/perl is 5.6.1 and /usr/local/bin/perl is 5.8.0 with the same compile flags (other than ithreads as previously mentioned) and the same version of gcc under linux. It was an integer conversion usage in Damian's Text::Reformat module that brought this to light. Something funny is definitely going on.

      printf doesn't really care whether the arguments are integers or not, it cares about what the size flags immediately precede. If you use the following code:
      use warnings; printf "%4.4i\n", 1; printf "%4.4li\n", 1; printf "%4.4hi\n", 1; printf "%4.4qi\n", 1; printf "%4.4Li\n", 1; printf "%4.4lli\n", 1;
      You then almost get the results that are predicted in the perldocs:
      0001 0001 0001 Invalid conversion in printf: "%q" at - line 5. %4.4qi 0001 0001
      (the zeroes are there because 4.4 ensures that the number will be 4 digits long in the case of an int)

      Of course, there's still that rogue q that doesn't work in 5.6.1 or 5.8.0. However, since q should be the same as L and ll, it's not like Perl is lacking some feature--they just lie about q being a valid alias.

      Again: l and h should only be used when followed by i, never by f. Perl, C, and GNU all appear to agree on this.