草庐IT

c++ - Boost的lexical_cast从double到string的精度

coder 2024-02-14 原文

我正在使用一个库,不幸的是它使用 boost::lexical_castdouble 转换为 string

我需要能够明确地反射(reflect)我这边的行为,但我希望在不传播 boost 的情况下这样做。

使用 to_stringsprintf 或标准中包含的其他一些函数是否可以保证相同的行为?

最佳答案

boost 代码在这里结束:

            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;
            }

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

稍微简化并允许符合标准的现代标准库:

模仿 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)每次都会产生完全相同的输出。

关于c++ - Boost的lexical_cast从double到string的精度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48080749/

有关c++ - Boost的lexical_cast从double到string的精度的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  3. ruby - 从 String#split 返回的零长度字符串 - 2

    在Ruby1.9.3(可能还有更早的版本,不确定)中,我试图弄清楚为什么Ruby的String#split方法会给我某些结果。我得到的结果似乎与我的预期相反。这是一个例子:"abcabc".split("b")#=>["a","ca","c"]"abcabc".split("a")#=>["","bc","bc"]"abcabc".split("c")#=>["ab","ab"]在这里,第一个示例返回的正是我所期望的。但在第二个示例中,我很困惑为什么#split返回零长度字符串作为返回数组的第一个值。这是什么原因呢?这是我所期望的:"abcabc".split("a")#=>["bc"

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  6. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  7. ruby-on-rails - ruby on rails 模型验证中的浮点精度 - 2

    我正在尝试使用正则表达式验证美元金额:^[0-9]+\.[0-9]{2}$这工作正常,但每当用户提交表单并且美元金额以0(零)结尾时,ruby(或rails?)将0砍掉。所以500.00变成500.0,因此正则表达式验证失败。有没有办法让ruby​​/rails保持用户输入的格式,而不管尾随零? 最佳答案 我假设您的美元金额是小数类型。因此,用户在字段中输入的任何值在保存到数据库之前都会从字符串转换为适当的类型。验证适用于已转换为数字类型的值,因此在您的情况下,正则表达式并不是真正合适的验证过滤器。不过,您有几种可能性可以解决这个问

  8. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  9. ruby 正则表达式 : replace double slashes in URL - 2

    除了协议(protocol)定义中的斜杠('http[s]://'、'ftp://'等)之外,我想替换URL中的所有多个斜杠。我该怎么做?此代码无一异常(exception)地替换:url.gsub(/\/\/+/,'/') 最佳答案 您只需排除任何以:开头的匹配项url.gsub(/([^:])\/\//,'\1/') 关于ruby正则表达式:replacedoubleslashesinURL,我们在StackOverflow上找到一个类似的问题: http

  10. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

随机推荐