Boost logo

Boost :

From: Paul A. Bristow (boost_at_[hidden])
Date: 2003-08-17 16:04:14


Curiously I have just posted a description of what may be the cause of this.

Attached...

My suggested remedy relies on the correct value for numeric_limits::digits (not
digits10)

Paul

Paul A Bristow, Prizet Farmhouse, Kendal, Cumbria, LA8 8AB UK
+44 1539 561830 Mobile +44 7714 33 02 04
mailto:pbristow_at_[hidden]

| -----Original Message-----
| From: boost-bounces_at_[hidden]
| [mailto:boost-bounces_at_[hidden]]On Behalf Of Aleksandr Golovanov
| Sent: Saturday, August 16, 2003 2:24 AM
| To: boost_at_[hidden]
| Subject: [boost] Re: lexical_cast
|
|
|
| "Ross Smith" <rosss_at_[hidden]> wrote in message
| news:bhgued$s99$1_at_sea.gmane.org...
| > Aleksandr Golovanov wrote:
| > > "Ross Smith" <rosss_at_[hidden]> wrote in message
| > > news:bher8m$gnq$1_at_sea.gmane.org...
| > >
| > >>Aleksandr Golovanov wrote:
| > >>
| > >>>Yesterday, I ran into a small problem, lexical_cast accepts copy
| instead of
| > >>>(const)? reference to a source. I have a class which I would prefer to
| be
| > >>>noncopyable and castable with laxical_cast at the same time.
| > >>
| > >>Wrap the object in boost::cref().
| > >
| > > Unfortunately, cref won't work because lexical_cast propagates the
| source
| > > type as a template parameter to various helper/standard templates:
| >
| > You're wrong; it works perfectly well. I tried it before I posted the
| > suggestion.
| >
|
| Following working example shows one possible case when application of
| boost::cref leads to a wrong result.
| Compiler VC6 SP5; lexical_cast from 1.30.0 boost release.
|
| #include "boost/lexical_cast.hpp"
| #include "boost/ref.hpp"
| #include <iostream>
| #include <string>
| #include <limits>
|
| class big_decimal
| {
| public:
| big_decimal() : m_val( 1.23456789 ) {}
| public:
| double m_val;
| };
|
| namespace std {
| class numeric_limits<big_decimal>
| : public numeric_limits<double>
| {
| };
| }
|
| std::ostream& operator<<( std::ostream& s, big_decimal const& arg )
| {
| return s << arg.m_val;
| }
|
| int main()
| {
| big_decimal dec;
| std::cout << boost::lexical_cast<std::string>( dec ) << "\n";
| std::cout << boost::lexical_cast<std::string>( boost::cref( dec ) ) <<
| "\n";
| return 0;
| }
|
| Result of execution:
|
| 1.23456789
| 1.23457
|
| Conclusion: usage of boost::cref with lexical_cast may lead to wrong results
| in the current implementation.
|
| --
| Thanks.
| Aleksandr Golovanov,
| MetaCommunications Engineering.
|
|
|
| _______________________________________________
| Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
|
|


attached mail follows:


I note that the 'precision' number of digits in lexical cast is obtained from
digits10 +1

if(std::numeric_limits<Target>::is_specialized)
{
         stream.precision(std::numeric_limits<Target>::digits10 + 1);
}

If, as I believe correct, the objective is to get all digits that can be
significant, and can be read back in without loss of precision, this isn't
always quite right according to:

"Lecture notes on the status of IEEE 754 standard for binary floating point
arithmetic"
William Kahan
http://http.cs.berkley.edu/~wkahan/ieee754status/ieee754.ps
gives formula for number of decimal digits which are guaranteed to be
correct on output and required for input to achieve maximum possible precision
as a function of the number of significand bits (given by
std::number_limits<FPType>::digits).

In C++ the full formula is:

        int significant_digits10 = int(ceil(1 + float_significand_digits * log10Two));

and using this formula :

std::numeric_limits<float>::digits = 24 significand bits
std::numeric_limits<float>::digits10 = 6
floor(float_significand_digits -1) * log10(2) = 6
ceil(1 + float_significand_digits * log10(2) = 9 all significant bits

(note that the existing code gives 7 here, which is 2 too few)

std::numeric_limits<double>::digits = 53
std::numeric_limits<double>::digits10 = 15
floor(double_significand_digits -1) * log10(2) = 15
ceil(1 + double_significand_digits * log10(2)) = 17

(note that the existing lecial_cast.hpp code gives 16 here, which is 1 too few)

32 significand bits digits10 = 6 significant_digits10 = 9
53 significand bits digits10 = 15 significant_digits10 = 17
64 significand bits digits10 = 18 significant_digits10 = 21
106 significand bits digits10 = 31 significant_digits10 = 33
113 significand bits digits10 = 33 significant_digits10 = 36
128 significand bits digits10 = 38 significant_digits10 = 40

(note that the rest are a few too few)

I have proposed before that numeric limits should have another item called,
perhaps,

significant_digits10 returning these useful values,
but meanwhile I suggest that following the style of boost/limits.h

  BOOST_STL_DECLARE_LIMITS_MEMBER(int, digits10, (digits * 301) / 1000);
                                // log 2 = 0.301029995664...

The integer fraction 301/1000 is needed to avoid suggeating to the compiler that
it should do a floating point calculation (which silently fails!)

so the following formula is used instead:

int const sig_digits10 = 2 + std::numeric_limits<FPType>::digits * 301/1000; //
log10(2.)

This gives the same result without using the ceil function which might not be
computed at compile time.

So in lexical_cast, substitute for example the above fragment with:

if(std::numeric_limits<Target>::is_specialized)
{ // Show all significant decimal digits, 2 or 3 more than digits10.
         stream.precision(2 + std::numeric_limits<Target>::digits * 301/1000);
} // & similarly for Source

A suggested revision and test attached, for example showing float & double now
have extra decimal digits.

Boost release 30 outputs:
1.414214
1.414213562373095

Revised version outputs:

1.41421354
1.4142135623730951

And it is thus now possible to convert float & double to a string and back again
to give exactly the same as the original float & double (which the current
version sometimes does not - a pit for the unwary).

Paul

Paul A Bristow, Prizet Farmhouse, Kendal, Cumbria, LA8 8AB UK
+44 1539 561830 Mobile +44 7714 33 02 04
mailto:pbristow_at_[hidden]






Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk