Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: LC_NUMERIC=fr_FR and floating point arithmetics



On 24 Feb, Stephane CHAZELAS wrote:
> $ LC_NUMERIC=fr_FR ksh93 -c 'float a=1; echo $(( a / 3 ))'
> 0,333333333333
> $ LC_NUMERIC=fr_FR zsh -c 'float a=1; echo $(( a / 3 ))'
> 0,33333333333333331.
>
> zsh seems to assume that "." is the decimal separator which is
> not correct in a french locale.
> 
> I agree this is confusing. A ksh93 script such as
> echo $(( 1. / 3 ))
>
> won't work under french locale.
> (must be echo $(( 1, / 3 )) )

Which is worse in my opinion. $(( 1.1 )) really ought to be interpreted
as a 1 point 1 regardless of locale - it is otherwise impossible to use
decimals in portable shell scripts.

If we didn't have backward and ksh compatibility to consider, I would
be inclined to say that $a for floats and $(( ... )) should always use
the C locale and anyone wanting locale specific output could use printf
(see below) (implementing this is easy; just save the LC_NUMERIC locale
in convfloat()).

But keeping locale handling in output means that scalar parameters
can't be reused in a calculation. And making math evaluation accept
either `.' or whatever the locale dictates is not easy when you
consider that a comma in math evaluation is used as a separator:
  (( a=1, 3.4 ))
Also, because we allow this:
  % a='3 + 4'
  % echo $(( a ))
  7
you can't just interpret the comma in a parameter expansion. So I can't
think of any solution except limiting math evaluation to the C locale.

So what can we do?

I did a grep for mon_decimal_point in /usr/share/i18n/locales to see
if any other characters are used as decimal separators. It indicates
that Portugal use `$' as their decimal separator. Seems weird. Apart
from that it is , or . for the rest of the world except Burkina Faso
who apparently use something outside the normal ASCII range.

Currently, a bug prevents locale handling for printf (on at least some
platforms). The bug causing this is in math.c where it does:
	prev_locale = setlocale(LC_NUMERIC, NULL);
	setlocale(LC_NUMERIC, "POSIX");
The second setlocale call clobbers prev_locale - we have to use
dupstring(). The patch fixes this.

Oliver

diff -ur zsh-latest/Src/math.c localeprob2/Src/math.c
--- zsh-latest/Src/math.c	2002-12-19 10:58:19.000000000 +0000
+++ localeprob2/Src/math.c	2003-03-09 01:13:18.000000000 +0000
@@ -399,12 +399,12 @@
 		    /* it's a float */
 		    yyval.type = MN_FLOAT;
 #ifdef USE_LOCALE
-		    prev_locale = setlocale(LC_NUMERIC, NULL);
+		    prev_locale = dupstring(setlocale(LC_NUMERIC, NULL));
 		    setlocale(LC_NUMERIC, "POSIX");
 #endif
 		    yyval.u.d = strtod(ptr, &nptr);
 #ifdef USE_LOCALE
-		    setlocale(LC_NUMERIC, prev_locale);
+		    if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
 #endif
 		    if (ptr == nptr || *nptr == '.') {
 			zerr("bad floating point constant", NULL, 0);



Messages sorted by: Reverse Date, Date, Thread, Author