Closed p5pRT closed 12 years ago
Despite the documentation\, 'use locale' does not enable locale formatting for printf.
env LANG=nl_NL perl -e 'use locale; printf "%.2f\n"\, 12345.67'
prints "12345.67"\, not "12345\,67".
On Mon May 23 05:23:33 2005\, jv wrote:
Despite the documentation\, 'use locale' does not enable locale formatting for printf.
env LANG=nl_NL perl -e 'use locale; printf "%.2f\n"\, 12345.67'
prints "12345.67"\, not "12345\,67".
In perl 5.14\, 'perldoc -f printf' includes this:
### If "use locale" is in effect\, and POSIX::setlocale() has been called\, the character used for the decimal separator in formatted floating-point numbers is affected by the LC_NUMERIC locale. ###
'perldoc perllocale' includes this:
### The POSIX::localeconv() function allows you to get particulars of the locale-dependent numeric formatting information specified by the current "LC_NUMERIC" and "LC_MONETARY" locales. ###
Based on the example provided in 'perllocale'\, I wrote and ran this program:
### $ cat 35949.pl #!/usr/local/bin/perl use strict; use warnings; use POSIX qw(locale_h);
my $locale_values = localeconv(); for (sort keys %$locale_values) { printf "%-20s = %s\n"\, $_\, $locale_values->{$_} }
$ env LANG=nl_NL perl ./35949.pl
currency_symbol = EUR
decimal_point = .
frac_digits = 2
int_curr_symbol = EUR
int_frac_digits = 2
mon_decimal_point = \,
mon_grouping =
mon_thousands_sep =
n_cs_precedes = 1
n_sep_by_space = 1
n_sign_posn = 2
negative_sign = -
p_cs_precedes = 1
p_sep_by_space = 1
p_sign_posn = 1
###
I note that the formatting requested by the original poster i handled by 'mon_decimal_point' rather than by 'decimal_point'. My hunch is that this is governed by the POSIX LC_MONETARY standard rather than LC_NUMERIC standard. But\, as cited above\, 'perldoc printf' only promises to obey LC_NUMERIC -- not both LC_MONETARY and LC_NUMERIC.
'perldoc perllocale' has a bit more to say on this:
### The C standard defines the "LC_MONETARY" category\, but not a function that is affected by its contents. ... Consequently\, Perl takes no notice of it. If you really want to use "LC_MONETARY"\, you can query its contents--see "The localeconv function"--and use the information that it returns in your application's own formatting of currency amounts. ###
My conclusion is that 'printf' makes no promise to obey LC_MONETARY and hence is working as documented. Perhaps the documentation could be clarified here\, but\, in the strict sense\, I don't think there is a bug.
Thank you very much. Jim Keenan
The RT System itself - Status changed from 'new' to 'open'
@cpansprout - Status changed from 'open' to 'rejected'
"James E Keenan via RT" \perlbug\-followup@​perl\.org writes:
On Mon May 23 05:23:33 2005\, jv wrote:
env LANG=nl_NL perl -e 'use locale; printf "%.2f\n"\, 12345.67'
prints "12345.67"\, not "12345\,67".
Thanks for your analysis. I hope it took less than six and a half years to complete :) .
In short: LC_MONETARY does not apply. "%.2f" is plain numeric formatting and has nothing to do with monetary formatting.
### If "use locale" is in effect\, and POSIX::setlocale() has been called\, the character used for the decimal separator in formatted floating-point numbers is affected by the LC_NUMERIC locale. ###
Exactly. In other words\, the decimal_point locale symbol should be applied to this formatting.
Based on the example provided in 'perllocale'\, I wrote and ran this program: ... $ env LANG=nl_NL perl ./35949.pl currency_symbol = EUR decimal_point = .
Interesting:
$ env LANG=nl_NL locale -k decimal_point decimal_point="\,"
This is what I'd expect for the dutch (and many other european) locale.
Might this indicate that Perl misunderstands the locale settings?
My conclusion is that 'printf' makes no promise to obey LC_MONETARY and hence is working as documented. Perhaps the documentation could be clarified here\, but\, in the strict sense\, I don't think there is a bug.
Even if this were true\, the expected result is a decimal comma\, not a period. If it is really more or less undefined\, it is also possible to use the good alternative.
-- Johan
@cpansprout - Status changed from 'rejected' to 'open'
On Sat Nov 19 06:37:15 2011\, jkeenan wrote:
On Mon May 23 05:23:33 2005\, jv wrote:
Despite the documentation\, 'use locale' does not enable locale formatting for printf.
env LANG=nl_NL perl -e 'use locale; printf "%.2f\n"\, 12345.67'
prints "12345.67"\, not "12345\,67".
In perl 5.14\, 'perldoc -f printf' includes this:
### If "use locale" is in effect\, and POSIX::setlocale() has been called\, the character used for the decimal separator in formatted floating-point numbers is affected by the LC_NUMERIC locale. ###
'perldoc perllocale' includes this:
### The POSIX::localeconv() function allows you to get particulars of the locale-dependent numeric formatting information specified by the current "LC_NUMERIC" and "LC_MONETARY" locales. ###
Based on the example provided in 'perllocale'\, I wrote and ran this program:
### $ cat 35949.pl #!/usr/local/bin/perl use strict; use warnings; use POSIX qw(locale_h);
my $locale_values = localeconv(); for (sort keys %$locale_values) { printf "%-20s = %s\n"\, $_\, $locale_values->{$_} }
$ env LANG=nl_NL perl ./35949.pl currency_symbol = EUR decimal_point = . frac_digits = 2 int_curr_symbol = EUR int_frac_digits = 2 mon_decimal_point = \, mon_grouping = mon_thousands_sep =
n_cs_precedes = 1 n_sep_by_space = 1 n_sign_posn = 2 negative_sign = - p_cs_precedes = 1 p_sep_by_space = 1 p_sign_posn = 1 ###I note that the formatting requested by the original poster i handled by 'mon_decimal_point' rather than by 'decimal_point'. My hunch is that this is governed by the POSIX LC_MONETARY standard rather than LC_NUMERIC standard. But\, as cited above\, 'perldoc printf' only promises to obey LC_NUMERIC -- not both LC_MONETARY and LC_NUMERIC.
Well\, perhaps that's the point: $ LC_ALL=nl_NL perl -MData::Dumper -MPOSIX=locale_h -wE 'use locale; say Dumper localeconv()' $VAR1 = { 'n_sep_by_space' => 1\, 'currency_symbol' => 'Eu'\, 'p_sign_posn' => 1\, 'negative_sign' => '-'\, 'int_curr_symbol' => 'EUR '\, 'frac_digits' => 2\, 'p_sep_by_space' => 1\, 'n_cs_precedes' => 1\, 'p_cs_precedes' => 1\, 'decimal_point' => '.'\, \<\<\<\<\<\<\<\<\<\<\<\<\< 'mon_grouping' => ''\, 'int_frac_digits' => 2\, 'n_sign_posn' => 2\, 'mon_thousands_sep' => ' '\, 'mon_decimal_point' => '\,' };
Whereas: $ perl -MData::Dumper -MPOSIX=locale_h -wE 'setlocale(LC_ALL\, "nl_NL"); say Dumper localeconv()' $VAR1 = { 'n_sep_by_space' => 1\, 'currency_symbol' => 'Eu'\, 'p_sign_posn' => 1\, 'negative_sign' => '-'\, 'int_curr_symbol' => 'EUR '\, 'frac_digits' => 2\, 'p_sep_by_space' => 1\, 'grouping' => ''\, 'n_cs_precedes' => 1\, 'p_cs_precedes' => 1\, 'int_frac_digits' => 2\, 'decimal_point' => '\,'\, \<\<\<\<\<\<\<\<\<\<\<\<\<\< 'mon_grouping' => ''\, 'n_sign_posn' => 2\, 'mon_thousands_sep' => ' '\, 'mon_decimal_point' => '\,' };
It looks like "use locale;" ignores LC_NUMERIC and that's not what the documentation says.
Good luck\, -- Abe.
On Sun\, Nov 20\, 2011 at 2:18 PM\, Johan Vromans \jvromans@​squirrel\.nl wrote:
"James E Keenan via RT" \perlbug\-followup@​perl\.org writes:
### If "use locale" is in effect\, and POSIX::setlocale() has been called\, the character used for the decimal separator in formatted floating-point numbers is affected by the LC_NUMERIC locale. ###
Exactly. In other words\, the decimal_point locale symbol should be applied to this formatting.
Based on the example provided in 'perllocale'\, I wrote and ran this program: ... $ env LANG=nl_NL perl ./35949.pl currency_symbol = EUR decimal_point = .
Interesting:
$ env LANG=nl_NL locale -k decimal_point decimal_point="\,"
This is what I'd expect for the dutch (and many other european) locale.
Might this indicate that Perl misunderstands the locale settings?
The devil may be in the details here. perllocale also says\, near the top:
*The current locale is set at execution time by setlocale()\<http://perldoc.perl.org/perllocale.html#The-setlocale-function>described below. If that function hasn't yet been called in the course of the program's execution\, the current locale is that which was determined by the ENVIRONMENT \<http://perldoc.perl.org/perllocale.html#ENVIRONMENT> in effect at the start of the program\, except that LC_NUMERIC\<http://perldoc.perl.org/perllocale.html#Category-LC_NUMERIC%3a-Numeric-Formatting>is always initialized to the C locale (mentioned under Finding locales \<http://perldoc.perl.org/perllocale.html#Finding-locales>). If there is no valid environment\, the current locale is undefined. It is likely\, but not necessarily\, the "C" locale. * So unless setlocale() is explicitly called\, it shouldn't matter what LC_* or LANG are set to in the environment\, LC_NUMERIC always would be expected to return a dot for decimal_point.
These snippets bear that out (note: LANG and LC* are all unset in the invoking environment):
# default $ perl -E 'use POSIX; say "$_ => ${ localeconv() }{$_}" for qw/mon_thousands_sep decimal_point/;' mon_thousands_sep => decimal_point => .
# specified LANG but with default LC_NUMERIC $ env LANG=nl_NL.roman8 perl -E 'use POSIX; say "$_ => ${ localeconv() }{$_}" for qw/mon_thousands_sep decimal_point/;' mon_thousands_sep => . decimal_point => .
# force LC_NUMERIC to match LANG $ env LANG=nl_NL.roman8 perl -E 'use POSIX; POSIX::setlocale(&POSIX::LC_NUMERIC\,$ENV{LANG}); say "$_ => ${ localeconv() }{$_}" for qw/mon_thousands_sep decimal_point/;' mon_thousands_sep => . decimal_point => \,
--Phil
On 11/22/2011 12:39 PM\, Philip Monsen wrote:
On Sun\, Nov 20\, 2011 at 2:18 PM\, Johan Vromans \<jvromans@squirrel.nl \mailto​:jvromans@​squirrel\.nl> wrote:
"James E Keenan via RT" \<perlbug\-followup@​perl\.org \<mailto​:perlbug\-followup@​perl\.org>> writes​: > \#\#\# > If "use locale" is in effect\, and POSIX​::setlocale\(\) > has been called\, the character used for the decimal separator > in formatted floating\-point numbers is affected by the > LC\_NUMERIC locale\. > \#\#\# Exactly\. In other words\, the decimal\_point locale symbol should be applied to this formatting\. > Based on the example provided in 'perllocale'\, I wrote and ran this > program​: > \.\.\. > $ env LANG=nl\_NL perl \./35949\.pl \<http​://35949\.pl> > currency\_symbol = EUR > decimal\_point = \. Interesting​: $ env LANG=nl\_NL locale \-k decimal\_point decimal\_point="\," This is what I'd expect for the dutch \(and many other european\) locale\. Might this indicate that Perl misunderstands the locale settings?
The devil may be in the details here. perllocale also says\, near the top:
/The current locale is set at execution time by setlocale() \<http://perldoc.perl.org/perllocale.html#The-setlocale-function> described below. If that function hasn't yet been called in the course of the program's execution\, the current locale is that which was determined by the ENVIRONMENT \<http://perldoc.perl.org/perllocale.html#ENVIRONMENT> in effect at the start of the program\, except that |LC_NUMERIC \<http://perldoc.perl.org/perllocale.html#Category-LC_NUMERIC%3a-Numeric-Formatting>| is always initialized to the C locale (mentioned under Finding locales \<http://perldoc.perl.org/perllocale.html#Finding-locales>). If there is no valid environment\, the current locale is undefined. It is likely\, but not necessarily\, the "C" locale. / So unless setlocale() is explicitly called\, it shouldn't matter what LC_* or LANG are set to in the environment\, LC_NUMERIC always would be expected to return a dot for decimal_point.
These snippets bear that out (note: LANG and LC* are all unset in the invoking environment):
# default $ perl -E 'use POSIX; say "$_ => ${ localeconv() }{$_}" for qw/mon_thousands_sep decimal_point/;' mon_thousands_sep => decimal_point => .
# specified LANG but with default LC_NUMERIC $ env LANG=nl_NL.roman8 perl -E 'use POSIX; say "$_ => ${ localeconv() }{$_}" for qw/mon_thousands_sep decimal_point/;' mon_thousands_sep => . decimal_point => .
# force LC_NUMERIC to match LANG $ env LANG=nl_NL.roman8 perl -E 'use POSIX; POSIX::setlocale(&POSIX::LC_NUMERIC\,$ENV{LANG}); say "$_ => ${ localeconv() }{$_}" for qw/mon_thousands_sep decimal_point/;' mon_thousands_sep => . decimal_point => \,
--Phil
There are several bugs dealing with locales and LC_NUMERIC in particular that I have noticed in reading the code. I was somewhat surprised to learn that it works at all.
Thanks for your bug report.
After some investigation\, and some bug fixes\, I am closing this ticket\, as it works as designed. Perl goes to some trouble to make the decimal point a period even in the face of a locale to the contrary. To override this\, a setlocale() call is required\, as Phillip said. I do not know the motivation behind this\, but it has worked that way for a long time. Some of the text of perllocale.pod was modified by me in 5.14 to clarify that the setlocale() call is needed (based on reading the code)\,though other text in it already indicated this.
The patches that I knew about have now been applied. There may be bugs remaining\, they're not the one described in this ticket. I don't have an nl locale on my box\, but I do have a de one\, and changing the program in the original ticket to use setlocale()\, gets it to work.
original ticket: LANG=de_DE.utf8 perl -e 'use locale; printf "%.2f\n"\, 12345.67' 12345.67
modified so works as documentd: LANG=de_DE.utf8 perl -e 'use locale; use POSIX qw(locale_h); setlocale(LC_ALL\, ""); printf "%.2f\n"\, 12345.67' 12345\,67
@khwilliamson - Status changed from 'open' to 'resolved'
Migrated from rt.perl.org#35949 (status was 'resolved')
Searchable as RT35949$