Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

sprintf %.g broken in bleadperl

0 views
Skip to first unread message

Enache Adrian

unread,
Nov 24, 2003, 2:08:34 AM11/24/03
to perl5-...@perl.org
On a -Duselongdouble perl:

$ perl -le 'printf "%.40g ", 0.01; print'
0.009999999999999999999796712092658967918624
$ perl -le 'printf "%.40g", 0.01; print'
0.0100000000000000002082

Regards,
Adi

Andy Lester

unread,
Nov 24, 2003, 1:10:20 AM11/24/03
to perl5-...@perl.org

What do you suggest that the answer should be? Or just that they should
match?

If all we care is that they match, then here's the TODO test that we can
drop somewhere in bleadperl:

TODO: {
local $TODO = "Not yet fixed."

my $withspace = sprintf( "%.40g ", 0.01 );
my $nospace = sprintf( "%.40g", 0.01 );
is( $withspace, "$nospace ",
"Having a trailing space in the mask doesn't break sprintf" );
}

xoa


--
Andy Lester => an...@petdance.com => www.petdance.com => AIM:petdance

Rafael Garcia-Suarez

unread,
Nov 24, 2003, 8:43:20 AM11/24/03
to Enache Adrian, perl5-...@perl.org, Ilya Zakharevich

Ilya Zakharevich recently optimized the special-case
where the pattern has the form "%.<number>[gf]" :

http://public.activestate.com/cgi-bin/perlbrowse?patch=21694

Looks like you found a bug in his implementation with long doubles.

Ilya Zakharevich

unread,
Nov 24, 2003, 4:29:28 PM11/24/03
to Rafael Garcia-Suarez, Enache Adrian, perl5-...@perl.org
On Mon, Nov 24, 2003 at 02:43:20PM +0100, Rafael Garcia-Suarez wrote:
> > $ perl -le 'printf "%.40g ", 0.01; print'
> > 0.009999999999999999999796712092658967918624
> > $ perl -le 'printf "%.40g", 0.01; print'
> > 0.0100000000000000002082
>
> Ilya Zakharevich recently optimized the special-case
> where the pattern has the form "%.<number>[gf]" :
>
> http://public.activestate.com/cgi-bin/perlbrowse?patch=21694
>
> Looks like you found a bug in his implementation with long doubles.

Is not this just a bug in Gconvert()? The question is: what is the
meaning of Gconvert() when NV is not double?

Until this is understood, one needs to disable the pass-to-Gconvert
branch with long doubles.

Another issue: is there a Configure answer for "check for a NaN"
function? Then one could disable Gconvert() in this case too (both
for sprintf() and stringification?). Or should one just use x >= 0 ||
x <= 0?

Hmm, yet third issue: how do NaNs behave w.r.t. <=>?

Ilya

Yitzchak Scott-Thoennes

unread,
Nov 25, 2003, 1:32:14 AM11/25/03
to Ilya Zakharevich, Rafael Garcia-Suarez, Enache Adrian, perl5-...@perl.org
On Mon, Nov 24, 2003 at 01:29:28PM -0800, Ilya Zakharevich <nospam...@ilyaz.org> wrote:
> On Mon, Nov 24, 2003 at 02:43:20PM +0100, Rafael Garcia-Suarez wrote:
> > > $ perl -le 'printf "%.40g ", 0.01; print'
> > > 0.009999999999999999999796712092658967918624
> > > $ perl -le 'printf "%.40g", 0.01; print'
> > > 0.0100000000000000002082
> >
> > Ilya Zakharevich recently optimized the special-case
> > where the pattern has the form "%.<number>[gf]" :
> >
> > http://public.activestate.com/cgi-bin/perlbrowse?patch=21694
> >
> > Looks like you found a bug in his implementation with long doubles.
>
> Is not this just a bug in Gconvert()? The question is: what is the
> meaning of Gconvert() when NV is not double?

If NV is long double, Gconvert is supposed to take long doubles if
Configure can figure out how (e.g. qgcvt or sprintf "%.*llg"). If
sprintf is not used, it is possible that whatever is used end up
converting differently than sprintf would have. Would this explain
the results here? What is perl -V:d_Gconvert?

> Until this is understood, one needs to disable the pass-to-Gconvert
> branch with long doubles.
>
> Another issue: is there a Configure answer for "check for a NaN"
> function? Then one could disable Gconvert() in this case too (both
> for sprintf() and stringification?). Or should one just use x >= 0 ||
> x <= 0?
>
> Hmm, yet third issue: how do NaNs behave w.r.t. <=>?

I think Configure probes for isnan for <=>'s sake, but sadly C99 isn't
all that widely available yet.

Ilya Zakharevich

unread,
Nov 25, 2003, 2:16:47 AM11/25/03
to Yitzchak Scott-Thoennes, Rafael Garcia-Suarez, Enache Adrian, perl5-...@perl.org
On Mon, Nov 24, 2003 at 10:32:14PM -0800, Yitzchak Scott-Thoennes wrote:
> > Another issue: is there a Configure answer for "check for a NaN"
> > function? Then one could disable Gconvert() in this case too (both
> > for sprintf() and stringification?). Or should one just use x >= 0 ||
> > x <= 0?

> I think Configure probes for isnan for <=>'s sake, but sadly C99 isn't


> all that widely available yet.

The only thing which checks this I know of is Sablot. It is doing

int isnan__(double x)
{
#ifdef HAVE_ISNAN
return isnan(x);
#elif defined(WIN32)
return _isnan(x);
#else
# error "Don't know how to detect NaN on this platform"
#endif
}

int isinf__(double x)
{
#ifdef HAVE_ISINF
return isinf(x);
#elif defined (HAVE_FINITE)
return ! finite(x) && x == x;
#elif defined (WIN32)
return ! _finite(x) && x == x;
#elif defined (__EMX__)
return ! isfinite(x) && x == x;
#else
# error "Don't know how to detect Infinity on this platform"
#endif
}

Should not Perl be not worse than this?

Hope this helps,
Ilya

Ilya Zakharevich

unread,
Nov 25, 2003, 3:26:05 AM11/25/03
to Yitzchak Scott-Thoennes, Rafael Garcia-Suarez, Enache Adrian, perl5-...@perl.org
On Mon, Nov 24, 2003 at 11:16:47PM -0800, Ilya Zakharevich wrote:
> The only thing which checks this I know of is Sablot. It is doing
>
> int isnan__(double x)
> {
> #ifdef HAVE_ISNAN

Actually, I just scanned my build directory, and there is another
reference point: libxml2. Apparently, it borrowed some files from
TRIO project, which is apparently located at
http://ctrio.sourceforge.net/.

Here is the code (inside #if 0 branch):

TRIO_PUBLIC int
trio_isfinite
TRIO_ARGS1((number),
double number)
{
#if defined(TRIO_COMPILER_SUPPORTS_C99) && defined(isfinite)
/*
* C99 defines isfinite() as a macro.
*/
return isfinite(number);

#elif defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB)
/*
* Microsoft Visual C++ and Borland C++ Builder use _finite().
*/
return _finite(number);

#elif defined(USE_IEEE_754)
/*
* Examine IEEE 754 bit-pattern. For finity we do not care about the
* mantissa.
*/
int dummy;

return (! trio_is_special_quantity(number, &dummy));

#else
/*
* Fallback solution.
*/
return ((trio_isinf(number) == 0) && (trio_isnan(number) == 0));

#endif
}

For the rest of 22K of code, see trionan.c of libxml2...

Yours,
Ilya

Enache Adrian

unread,
Nov 25, 2003, 10:01:36 AM11/25/03
to Yitzchak Scott-Thoennes, Ilya Zakharevich, Rafael Garcia-Suarez, perl5-...@perl.org
On Mon, Nov 24, 2003 a.d., Yitzchak Scott-Thoennes wrote:
> If NV is long double, Gconvert is supposed to take long doubles if
> Configure can figure out how (e.g. qgcvt or sprintf "%.*llg"). If
> sprintf is not used, it is possible that whatever is used end up
> converting differently than sprintf would have. Would this explain
> the results here? What is perl -V:d_Gconvert?

No Gconvert is just fine. (it's qgcvt() here).
The bug is:

1. classical cast & punch.
Gconvert((double)nv, digits, 0, ebuf);
nv is a long double, and Gconvert takes a long double argument too.

2. the new %.g special case avoids all the stuff in sv.c:9227 and
below, which is setting the correct size to use.

I don't see how to fix 2) in the new patch :-)

Regards,
Adi

Yitzchak Scott-Thoennes

unread,
Nov 25, 2003, 9:51:24 AM11/25/03
to Ilya Zakharevich, Rafael Garcia-Suarez, Enache Adrian, perl5-...@perl.org
On Mon, Nov 24, 2003 at 11:16:47PM -0800, Ilya Zakharevich <nospam...@ilyaz.org> wrote:
> On Mon, Nov 24, 2003 at 10:32:14PM -0800, Yitzchak Scott-Thoennes wrote:
> > > Another issue: is there a Configure answer for "check for a NaN"
> > > function? Then one could disable Gconvert() in this case too (both
> > > for sprintf() and stringification?). Or should one just use x >= 0 ||
> > > x <= 0?
>
> > I think Configure probes for isnan for <=>'s sake, but sadly C99 isn't
> > all that widely available yet.

Either perl has improved since last I looked, or I misread the code
then. Looks like perl.h will always come up with some definition for
Perl_isnan, Perl_isinf, and Perl_isfinite. (Though I think the
last-ditch check (x)==NV_INF in Perl_isinf should also be checking for
negative infinity.)

Rafael Garcia-Suarez

unread,
Nov 28, 2003, 6:59:51 PM11/28/03
to Enache Adrian, Yitzchak Scott-Thoennes, Ilya Zakharevich, Rafael Garcia-Suarez, perl5-...@perl.org

/me neither. I've thus applied the fix below : (but better tuits welcome)

Change 21800 by rgs@rgs-home on 2003/11/28 22:38:40

Fix a regression introduced by change #21694 on sprintf()
with long doubles, by disabling the specific optimisation
path in this case ; remove a unnecessary cast ; add a new
test file for miscellaneous sprintf() test that don't fit
in the t/op/sprintf.t framework.

Affected files ...

... //depot/perl/MANIFEST#1110 edit
... //depot/perl/sv.c#697 edit
... //depot/perl/t/op/sprintf2.t#1 add

Differences ...

==== //depot/perl/MANIFEST#1110 (text) ====

@@ -2796,6 +2796,7 @@
t/op/splice.t See if splice works
t/op/split.t See if split works
t/op/sprintf.t See if sprintf works
+t/op/sprintf2.t See if sprintf works
t/op/srand.t See if srand works
t/op/stash.t See if %:: stashes work
t/op/stat.t See if stat works

==== //depot/perl/sv.c#697 (text) ====

@@ -8630,6 +8630,7 @@
}
}

+#ifndef USE_LONG_DOUBLE
/* special-case "%.<number>[gf]" */
if ( patlen <= 5 && pat[0] == '%' && pat[1] == '.'
&& (pat[patlen-1] == 'g' || pat[patlen-1] == 'f') ) {
@@ -8650,7 +8651,7 @@
return;
if (*pp == 'g') {
if (digits < sizeof(ebuf) - NV_DIG - 10) { /* 0, point, slack */
- Gconvert((double)nv, digits, 0, ebuf);
+ Gconvert(nv, digits, 0, ebuf);
sv_catpv(sv, ebuf);
if (*ebuf) /* May return an empty string for digits==0 */
return;
@@ -8665,6 +8666,7 @@
}
}
}
+#endif /* !USE_LONG_DOUBLE */

if (!args && svix < svmax && DO_UTF8(*svargs))
has_utf8 = TRUE;
@@ -9359,7 +9361,7 @@
if ( !(width || left || plus || alt) && fill != '0'
&& has_precis && intsize != 'q' ) { /* Shortcuts */
if ( c == 'g') {
- Gconvert((double)nv, precis, 0, PL_efloatbuf);
+ Gconvert(nv, precis, 0, PL_efloatbuf);
if (*PL_efloatbuf) /* May return an empty string for digits==0 */
goto float_converted;
} else if ( c == 'f' && !precis) {

0 new messages