Boost的lexical_cast从double到string Precision

时间:2018-01-03 15:53:04

标签: c++ boost number-formatting standard-library boost-lexicalcast

我正在使用不幸使用boost::lexical_castdouble转换为string的库。

我需要能够明确地反映出我这方面的行为,但我还是在没有传播boost的情况下这样做。

我可以使用to_stringsprintf或标准中包含的其他功能保证相同的行为吗?

2 个答案:

答案 0 :(得分:4)

所以经过几个小时的挖掘Boost模板后,这就是我所学到的:

  1. 执行字符串化的实际调用是:>>> li=list("He'llo") >>> li[2:4]=[''.join(li[2:4])] >>> li ['H', 'e', "'l", 'l', 'o']
  2. 这会在lexical_cast_do_cast<std::string, double>::lexical_cast_impl
  3. 中使用std::sprintf
  4. boost::detail::lexical_stream_limited_src<char, std::char_traits<char>, false>将用于插入boost::detail::lexical_stream_limited_src<char, std::char_traits<char>, true>::operator<<,传递double,指向已分配的begin缓冲区的指针,以及std::string,输入val,产生此调用:double
  5. 所以这里的精度字段来自std::sprintf(begin, "%.*g", static_cast<int>(boost::detail::lcast_get_precision<double>()), val),它将使用boost::details::lcast_precision<double>::value;如果std::numeric_limits<double>is_specialized,则falseis_exactfalseradix2大于{{1}然后digits将评估为:0
  6. 因此boost::details::lcast_precision<double>::value分配的2UL + std::numeric_limits<double>::digits * 30103UL / 100000ULbegin是输入双精度,  string产生的最终结果相当于:

    val

    这显然严重依赖于实现。但是在我的系统上,这将产生完全相同的效果。

答案 1 :(得分:3)

升级代码在此结束:

            bool shl_real_type(double val, char* begin) {
                using namespace std;
                finish = start +
#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION)
                    sprintf_s(begin, CharacterBufferSize,
#else
                    sprintf(begin, 
#endif
                    "%.*g", static_cast<int>(boost::detail::lcast_get_precision<double>()), val);
                return finish > start;
            }

您很幸运,因为精度是USUALLY编译时常量(除非boost配置BOOST_LCAST_NO_COMPILE_TIME_PRECISION)。

简化一点并允许符合标准的现代标准库:

Mimicking Boost Lexicalcast

#include <cstdio>
#include <limits>
#include <string>

namespace {
    template <class T> struct lcast_precision {
        typedef std::numeric_limits<T> limits;

        static constexpr bool use_default_precision  = !limits::is_specialized || limits::is_exact;
        static constexpr bool is_specialized_bin     = !use_default_precision && limits::radix == 2 && limits::digits > 0;

        static constexpr bool is_specialized_dec     = !use_default_precision && limits::radix == 10 && limits::digits10 > 0;
        static constexpr unsigned int precision_dec  = limits::digits10 + 1U;
        static constexpr unsigned long precision_bin = 2UL + limits::digits * 30103UL / 100000UL;

        static constexpr unsigned value = is_specialized_bin 
            ? precision_bin 
            : is_specialized_dec? precision_dec : 6;
    };

    std::string mimicked(double v) {
        constexpr int prec = static_cast<int>(lcast_precision<double>::value);

        std::string buf(prec+10, ' ');
        buf.resize(sprintf(&buf[0], "%.*g", prec, v));
        return buf;
    }
}

回归测试

比较结果并检查假设:

<强> Live On Coliru

#include <cstdio>
#include <limits>
#include <string>

namespace {
    template <class T> struct lcast_precision {
        typedef std::numeric_limits<T> limits;

        static constexpr bool use_default_precision  = !limits::is_specialized || limits::is_exact;
        static constexpr bool is_specialized_bin     = !use_default_precision && limits::radix == 2 && limits::digits > 0;

        static constexpr bool is_specialized_dec     = !use_default_precision && limits::radix == 10 && limits::digits10 > 0;
        static constexpr unsigned int precision_dec  = limits::digits10 + 1U;
        static constexpr unsigned long precision_bin = 2UL + limits::digits * 30103UL / 100000UL;

        static constexpr unsigned value = is_specialized_bin 
            ? precision_bin 
            : is_specialized_dec? precision_dec : 6;
    };

    std::string mimicked(double v) {
        constexpr int prec = static_cast<int>(lcast_precision<double>::value);

        std::string buf(prec+10, ' ');
        buf.resize(sprintf(&buf[0], "%.*g", prec, v));
        return buf;
    }
}

#include <cmath>
#include <iomanip>
#include <iostream>
#include <string>

#include <boost/lexical_cast.hpp>

#ifdef BOOST_LCAST_NO_COMPILE_TIME_PRECISION
#error BOOM
#endif

#define TEST(x)                                                                                                        \
    do {                                                                                                               \
        std::cout << std::setw(45) << #x << ":\t" << (x) << "\n";                                                      \
    } while (0)

std::string use_sprintf(double v) {
    std::string buf(32, ' ');
    buf.resize(std::sprintf(&buf[0], "%f", v));
    return buf;
}

void tests() {
    for (double v : {
            std::numeric_limits<double>::quiet_NaN(),
            std::numeric_limits<double>::infinity(),
           -std::numeric_limits<double>::infinity(),
            0.0,
           -0.0,
            std::numeric_limits<double>::epsilon(),
            M_PI })
    {
        TEST(v);
        TEST(std::to_string(v));
        TEST(use_sprintf(v));
        TEST(boost::lexical_cast<std::string>(v));
        TEST(mimicked(v));

        assert(mimicked(v) == boost::lexical_cast<std::string>(v));
    }
}

static std::locale DE("de_DE.utf8");

int main() {

    tests();

    std::cout << "==== imbue std::cout\n";
    std::cout.imbue(DE);

    tests();

    std::cout << "==== override global locale\n";
    std::locale::global(DE);

    tests();
}

打印

                                        v:  nan
                        std::to_string(v):  nan
                           use_sprintf(v):  nan
      boost::lexical_cast<std::string>(v):  nan
                              mimicked(v):  nan
                                        v:  inf
                        std::to_string(v):  inf
                           use_sprintf(v):  inf
      boost::lexical_cast<std::string>(v):  inf
                              mimicked(v):  inf
                                        v:  -inf
                        std::to_string(v):  -inf
                           use_sprintf(v):  -inf
      boost::lexical_cast<std::string>(v):  -inf
                              mimicked(v):  -inf
                                        v:  0
                        std::to_string(v):  0.000000
                           use_sprintf(v):  0.000000
      boost::lexical_cast<std::string>(v):  0
                              mimicked(v):  0
                                        v:  -0
                        std::to_string(v):  -0.000000
                           use_sprintf(v):  -0.000000
      boost::lexical_cast<std::string>(v):  -0
                              mimicked(v):  -0
                                        v:  2.22045e-16
                        std::to_string(v):  0.000000
                           use_sprintf(v):  0.000000
      boost::lexical_cast<std::string>(v):  2.2204460492503131e-16
                              mimicked(v):  2.2204460492503131e-16
                                        v:  3.14159
                        std::to_string(v):  3.141593
                           use_sprintf(v):  3.141593
      boost::lexical_cast<std::string>(v):  3.1415926535897931
                              mimicked(v):  3.1415926535897931
==== imbue std::cout
                                        v:  nan
                        std::to_string(v):  nan
                           use_sprintf(v):  nan
      boost::lexical_cast<std::string>(v):  nan
                              mimicked(v):  nan
                                        v:  inf
                        std::to_string(v):  inf
                           use_sprintf(v):  inf
      boost::lexical_cast<std::string>(v):  inf
                              mimicked(v):  inf
                                        v:  -inf
                        std::to_string(v):  -inf
                           use_sprintf(v):  -inf
      boost::lexical_cast<std::string>(v):  -inf
                              mimicked(v):  -inf
                                        v:  0
                        std::to_string(v):  0.000000
                           use_sprintf(v):  0.000000
      boost::lexical_cast<std::string>(v):  0
                              mimicked(v):  0
                                        v:  -0
                        std::to_string(v):  -0.000000
                           use_sprintf(v):  -0.000000
      boost::lexical_cast<std::string>(v):  -0
                              mimicked(v):  -0
                                        v:  2,22045e-16
                        std::to_string(v):  0.000000
                           use_sprintf(v):  0.000000
      boost::lexical_cast<std::string>(v):  2.2204460492503131e-16
                              mimicked(v):  2.2204460492503131e-16
                                        v:  3,14159
                        std::to_string(v):  3.141593
                           use_sprintf(v):  3.141593
      boost::lexical_cast<std::string>(v):  3.1415926535897931
                              mimicked(v):  3.1415926535897931
==== override global locale
                                        v:  nan
                        std::to_string(v):  nan
                           use_sprintf(v):  nan
      boost::lexical_cast<std::string>(v):  nan
                              mimicked(v):  nan
                                        v:  inf
                        std::to_string(v):  inf
                           use_sprintf(v):  inf
      boost::lexical_cast<std::string>(v):  inf
                              mimicked(v):  inf
                                        v:  -inf
                        std::to_string(v):  -inf
                           use_sprintf(v):  -inf
      boost::lexical_cast<std::string>(v):  -inf
                              mimicked(v):  -inf
                                        v:  0
                        std::to_string(v):  0,000000
                           use_sprintf(v):  0,000000
      boost::lexical_cast<std::string>(v):  0
                              mimicked(v):  0
                                        v:  -0
                        std::to_string(v):  -0,000000
                           use_sprintf(v):  -0,000000
      boost::lexical_cast<std::string>(v):  -0
                              mimicked(v):  -0
                                        v:  2,22045e-16
                        std::to_string(v):  0,000000
                           use_sprintf(v):  0,000000
      boost::lexical_cast<std::string>(v):  2,2204460492503131e-16
                              mimicked(v):  2,2204460492503131e-16
                                        v:  3,14159
                        std::to_string(v):  3,141593
                           use_sprintf(v):  3,141593
      boost::lexical_cast<std::string>(v):  3,1415926535897931
                              mimicked(v):  3,1415926535897931

请注意,mimickedboost::lexical_cast<std::string>(double)每次都会产生完全相同的输出。