提问人:f1msch 提问时间:6/27/2022 最后编辑:Jarod42f1msch 更新时间:6/28/2022 访问量:334
C++17 Trans int 到前缀为“0”字符串的快速方法
c++17 fast way for trans int to string with prefix '0'
问:
我想将 an 转换为 .
我知道在 c++17 下,更好的方法是使用 .int
string
std::to_string
但现在我想用几个“0”填充前缀。
例如,是“1”,但我希望结果是“00001”(总长度为 5)。int i = 1
std::to_string(i)
我知道使用或可能实现这一点。
但是哪个有更好的性能或其他方式?sprintf
stringstream
答:
0赞
ramsay
6/27/2022
#1
是的,使用 or 都可以将 int 转换为带前导的字符串。snprintf
stringstream
0
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
using std::chrono::milliseconds;
std::string ss_to_string(int data) {
std::stringstream ss;
ss << std::setfill('0') << std::setw(5) << data;
return ss.str();
}
std::string snprint_to_string(int data) {
char buffer[6];
snprintf(buffer, 6, "%05d", data);
return buffer;
}
int main(int argc, char **argv) {
auto t1 = high_resolution_clock::now();
for (int i = 0; i < 100000; i++) {
ss_to_string(1);
}
auto t2 = high_resolution_clock::now();
std::cout << "sstream version: "
<< duration_cast<milliseconds>(t2 - t1).count() << "ms\n";
auto t3 = high_resolution_clock::now();
for (int i = 0; i < 100000; i++) {
snprint_to_string(1);
}
auto t4 = high_resolution_clock::now();
std::cout << "snprintf version: "
<< duration_cast<milliseconds>(t4 - t3).count() << "ms\n";
}
在测量了版本和版本之后,事实证明实现效率更高。snprintf
stringstream
snprintf
sstream version: 60ms
snprintf version: 23ms
评论
2赞
Marek R
6/27/2022
请注意,在 C++ 中衡量性能并不像您想象的那么简单,因为存在“好像规则”。编译器可以删除大量经过测试的代码。因此,您的测试可能无效。你还使用了优化标志吗?
0赞
Marek R
6/27/2022
看起来差异更大(比例 4:1):quick-bench.com/q/dI0PGANZ-t1WKJyw-RkK2MVY37E
1赞
Mgetz
6/27/2022
我很想看看并包括std::format
std::to_chars
0赞
Marek R
6/27/2022
@Mgetz可悲的是,编译器对 的支持仍然很差。std::format
1赞
Ted Lyngmo
6/27/2022
@MarekR尤其是在 C++17 年。:-)
4赞
Howard Hinnant
6/28/2022
#2
如果你对你的域名有所了解,并且不需要检查错误,那么没有什么比滚动你自己的域名更快的了。例如,如果您知道 ur 始终在 [0, 99'999] 范围内,则可以:int
std::string
convert(unsigned i)
{
std::string r(5, '0');
char* s = r.data() + 4;
do
{
*s-- = static_cast<char>(i%10 + '0');
i /= 10;
} while (i > 0);
return r;
}
通用库没有做出这种假设的奢侈。
4赞
Marek R
6/28/2022
#3
就像在评论中提到的那样,首先测量! 我已经创建了具有多种可能实现的存储库来测试它们。
- std::ostringstream
- 斯普林特夫
- fmt::格式
- 标准::to_chars
- 完整的手动实现(复制表 Howard Hinnant) - 如果数字不适合 5 个字符,则失败。
请注意,不包括负数(预计会出现一些错误)!
这是现场演示(没有 fmt)。
#include <sstream>
#include <charconv>
#include <iomanip>
namespace fill5 {
namespace tag {
struct std_stream {};
struct std_to_chars {};
struct c_sprintf {};
struct manual {};
}
template<typename Tag>
std::string toString(int x);
namespace {
constexpr auto width = 5;
constexpr auto fill = '0';
std::string strBuf() {
std::string r;
r.resize(r.capacity());
return r;
}
}
template<>
std::string toString<tag::std_stream>(int x)
{
std::ostringstream r;
r << std::setw(width) << std::setfill(fill) << x;
return r.str();
}
template<>
std::string toString<tag::std_to_chars>(int value)
{
auto r = strBuf();
auto x = std::to_chars(r.data(), r.data() + r.size(), value);
r.resize(x.ptr - r.data());
if (r.size() < width) {
r.insert(0, width - r.size(), fill);
}
return r;
}
template<>
std::string toString<tag::c_sprintf>(int value)
{
auto r = strBuf();
r.resize(std::sprintf(r.data(), "%05d", value));
return r;
}
template<>
std::string toString<tag::manual>(int value)
{
std::string r(5, '0');
char* s = r.data() + 4;
do
{
*s-- = char(value % 10) + '0';
value /= 10;
} while (value > 0);
return r;
}
}
template <typename ImplTag>
void fill5ToString(benchmark::State& state) {
constexpr int data[] {0, 1, 5, 13, 43, 343, 5344, 4234, 55555, 243422342};
for (auto _ : state) {
for (auto x : data) {
benchmark::DoNotOptimize(x);
auto r = fill5::toString<ImplTag>(x);
benchmark::DoNotOptimize(r);
}
}
}
using namespace fill5::tag;
BENCHMARK(fill5ToString<std_stream>);
BENCHMARK(fill5ToString<c_sprintf>);
BENCHMARK(fill5ToString<std_to_chars>);
BENCHMARK(fill5ToString<manual>);
以下是我的机器(涵盖版本)的结果:fmt
% ./perf/Release/perf
2022-06-28T01:36:36+02:00
Running ./perf/Release/perf
Run on (16 X 3600 MHz CPU s)
CPU Caches:
L1 Data 32 KiB (x8)
L1 Instruction 32 KiB (x8)
L2 Unified 256 KiB (x8)
L3 Unified 16384 KiB (x1)
Load Average: 1.13, 1.28, 1.12
----------------------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------------------
fill5ToString<std_stream> 1782 ns 1782 ns 388470
fill5ToString<c_sprintf> 648 ns 648 ns 1070107
fill5ToString<std_to_chars> 227 ns 227 ns 3063779
fill5ToString<fmt> 442 ns 442 ns 1584815
fill5ToString<manual> 46.7 ns 46.7 ns 14455075
正如预期的那样,手动实现摇摆不定。 版本最好来自简单的实现,版本是“像一样快”。fmt
std::ostringstream
评论
sprintf()
stringstream
snprintf()
std::to_chars
?fmt
std::stringstream
int
std::stringstream
first