$ perl -le 'printf "%.40g ", 0.01; print'
0.009999999999999999999796712092658967918624
$ perl -le 'printf "%.40g", 0.01; print'
0.0100000000000000002082
Regards,
Adi
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
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
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.
> 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
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
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
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.)
/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) {