使用iostream根据用户的区域设置格式化数字的性能方法? [英] Performant way of formatting numbers according to the user's locale with iostream?

查看:94
本文介绍了使用iostream根据用户的区域设置格式化数字的性能方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我们的应用程序的一个部分,需要根据用户的区域设置格式化数字。



接口基本上看起来像这样:

  std :: string format_number(double number); 

旧实现看起来像这样:

  std :: string format_number(double number){
using namespace std;
static const locale user_loc();
ostringstream fmtstream;
fmtstream.imbue(user_loc);
fmtstream<< std :: fixed;
fmtstream<<数;
return fmtstream.str();
}



现在我们注意到,使用我们的编译器使用 num_put 直接:



<$ p $此功能的速度约为10-20% p> std :: string format_number(double number){
using namespace std;
static const locale user_loc();
ostringstream fmtstream;
fmtstream.imbue(user_loc);
fmtstream<< std :: fixed;
typedef char * CharBufOutIt;
typedef num_put< char,CharBufOutIt> np_t;
np_t const& npf = use_facet< np_t>(user_loc);
char buf [127];
const CharBufOutIt begin =& buf [0];
const CharBufOutIt end = npf.put(/ * out = * / begin,/ * format = * / fmtstream,/ * fill = * /'',number)
return std :: string(begin,end);
}

这似乎不太理想:


$ b b

  • 我们需要构造一个流对象,只是为了填充它的语言环境并将其用于格式化标志。

  • 我们不能缓存流对象, put 将在传递的格式流上调用 width(0)



是另一个诡计加快速度,或者这是我们最希望从iostream中退出?



请注意,我们不能在这里使用sprintf,因为我们需要确保使用完整的语言环境(包括千位分隔符),并且printf不输出千分隔符。



关键是不要使用格式化标志的流,因为 / * format = * / 参数 put 只是 访问它的标志和语言环境,因此更基本的对象足够:

  std :: string version_3(double number){
using namespace std;
static const locale user_loc();
ios fmtios(NULL); //而不是全流
fmtios.imbue(user_loc);
fmtios.setf(ios_base :: fixed);
typedef char * CharBufOutIt;
typedef num_put< char,CharBufOutIt> np_t;
np_t const& npf = use_facet< np_t>(user_loc);
char buf [127];
const CharBufOutIt begin =& buf [0];
const CharBufOutIt end = npf.put(/ * out = * / begin,/ * format = * / fmtios,/ * fill = * /'',value);
return std :: string(begin,end);
}

我过早地拒绝了这个版本,因为我不知道你可以合法将nullptr作为流缓冲区传递给 basic_ios 文档的init 显示您可以,虽然会导致 badbit 被设置,但是只要我能够告诉 put 不关心状态位。 p>

In one part of our application there is the requirement to format numbers according to the user's locale.

The interface essentially looks like this:

std::string format_number(double number);

The old implementation looked like this:

std::string format_number(double number) {
    using namespace std;
    static const locale user_loc("");
    ostringstream fmtstream;
    fmtstream.imbue(user_loc);
    fmtstream << std::fixed;
    fmtstream << number;
    return fmtstream.str();
}

We have now noticed that with our compiler (MSVC 2005), we can get a approx 10-20% speed up of this function (measured in isolation) by using num_put directly:

std::string format_number(double number) {
    using namespace std;
    static const locale user_loc("");
    ostringstream fmtstream;
    fmtstream.imbue(user_loc);
    fmtstream << std::fixed;
    typedef char* CharBufOutIt;
    typedef num_put<char, CharBufOutIt> np_t;
    np_t const& npf = use_facet<np_t>(user_loc);
    char buf[127];
    const CharBufOutIt begin = &buf[0];
    const CharBufOutIt end = npf.put(/*out=*/begin, /*format=*/fmtstream, /*fill=*/' ', number);
    return std::string(begin, end);
}

This still seems suboptimal:

  • We need to construct a stream object just to imbue it with the locale and use it for the formatting flags.
  • We cannot cache the stream object, as put will call width(0)on the passed format stream.

Is the another "trick" to speed this up, or is this the most we can hope to get out of iostream?

And note that we cannot use sprintf here, as we need to make sure the full locale, including thousands separator, is used, and printf doesn't output the thousand separator.

解决方案

Thanks to user 0x499602D2's answer I have been able to speed this up some more (even with a locale facet).

The point is to not use a stream for the formatting flags, since the /*format=*/ parameter to put is only accessed for it's flags and locale, so a more basic object suffices:

std::string version_3(double number) {
    using namespace std;
    static const locale user_loc("");
    ios fmtios(NULL); // instead of a full stream
    fmtios.imbue(user_loc);
    fmtios.setf(ios_base::fixed);
    typedef char* CharBufOutIt;
    typedef num_put<char, CharBufOutIt> np_t;
    np_t const& npf = use_facet<np_t>(user_loc);
    char buf[127];
    const CharBufOutIt begin = &buf[0];
    const CharBufOutIt end = npf.put(/*out=*/begin, /*format=*/fmtios, /*fill=*/' ', value);
    return std::string(begin, end);
}

I prematurely had dismissed this version because I was not aware that you can actually legally pass a nullptr as stream buffer to basic_ios. The docs for init show you can, although it will result in badbit being set, but as far as I'm able to tell put doesn't care about the state bits.

这篇关于使用iostream根据用户的区域设置格式化数字的性能方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆