- assigned_to: nobody --> complement
- status: open --> open-accepted
Running the following code
#include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char* argv[])
{
double d=1.23;
int precision=2;
cout<<scientific<<setprecision(precision)<<d<<endl;
return 0;
}
STLport 5.1 gives
-1.23e+000
STLport 5.2 gives
-1.20e+00
Microsoft's STL (VC6 and VC8) matches the STLport 5.1 behaviour.
Stroustrup states in The C++ Programming Language (21.4.3 of 3rd Edition which is what I have to hand) that for the scientific format "The precision specifies the maximum number of digits after the decimal point." It appears that STLport 5.2 is rounding to one power of ten too high, e.g. if I alter the second line of the code fragment to
int precision=1;
STLport 5.2 gives
-1.0e+00
I can't see a recent STLport bug report for something like this.
Just adding some information on a bit of further investigation I did.
stlport-5.1\src\num_put_float.cpp contained this fragment:
switch (flags & ios_base::floatfield) {
case ios_base::fixed:
bp = _Stl_fcvtR(x, (min) (precision, MAXFCVT), &decpt, &sign _STLP_CVT_BUFFER(cvtbuf));
break;
case ios_base::scientific :
bp = _Stl_ecvtR(x, (min) (precision + 1, MAXECVT), &decpt, &sign _STLP_CVT_BUFFER(cvtbuf));
break;
default :
bp = _Stl_ecvtR(x, (min) (precision, MAXECVT), &decpt, &sign _STLP_CVT_BUFFER(cvtbuf));
break;
}
stlport-5.2\src\num_put_float.cpp contains this:
switch (flags & ios_base::floatfield) {
case ios_base::fixed:
{
/* Here, number of digits represents digits _after_ decimal point.
* In order to limit static buffer size we have to give 2 different values depending on x value.
* For small values (abs(x) < 1) we need as many digits as requested by precision limited by the maximum number of digits
* which is min_exponent10 + digits10 + 2
* For bigger values we won't have more than limits::digits10 + 2 digits after decimal point. */
int digits10 = (x > -1.0 && x < 1.0 ? -limits::min_exponent10 + limits::digits10 + 2
: limits::digits10 + 2);
bp = _Stl_fcvtR(x, (min) (precision, digits10), &decpt, &sign, _STLP_CVT_BUFFER(cvtbuf) );
}
break;
case ios_base::scientific:
default:
/* Here, number of digits is total number of digits which is limited to digits10 + 2. */
{
int digits10 = limits::digits10 + 2;
bp = _Stl_ecvtR(x, (min) (precision, digits10), &decpt, &sign, _STLP_CVT_BUFFER(cvtbuf) );
}
break;
}
I guess that a fix might be to insert this code after case ios_base::scientific: (between lines 808 and 809 of stlport-5.2\src\num_put_float.cpp)
/* Here, number of digits is total number of digits which is limited to digits10 + 2. */
{
int digits10 = limits::digits10 + 2;
bp = _Stl_ecvtR(x, (min) (precision + 1, digits10), &decpt, &sign, _STLP_CVT_BUFFER(cvtbuf) );
}
break;
My reasoning is that 5.1 has precision + 1 because _ecvt doesn't distinguish between digits before and after decimal point but precision in scientific format is number of digits after decimal point. So, as there is one digit before the decimal point in this format, the total digits needed is precision + 1.
We may try this patch locally.
You almost right. 'Almost' because problem a bit deeper. It seems that I can use _Stl_fcvtR [treat precision as digits after point] (instead of _Stl_ecvtR), but ... but it lead to arbitrary buffer size in underlaying fcvt_r, if I write something like
str << scientific << setprecision(50) << limits::max();
Another problem is %g %G conversions. And more, not implemented %a %A at all (i.e. floatfield == ios_base::fixed | ios_base::scientific, as required by draft).
Hm, in glibc (2.11.1) fcvt_r work via snprintf, ecvt_r work via fcvt_r. Looks no reasons to keep in STLport ?cvt-based implementation: either use snprintf or write own float conversion implementation.
Problem of sprintf-based solution: it use global locale, not object-attached locale as required for C++.
Log in to post a comment.