将浮点数转换为字符串的最快C++方法

11
我遇到了将浮点数转换为字符串的问题,其中使用to_string太慢,因为我的数据可能涉及几百万个浮点数。
我已经有了如何快速写出这些数据的解决方案
然而,在解决了那个问题之后,我很快意识到浮点数转换为字符串会留下巨大的影响。
那么,除了使用其他非标准库之外,是否有任何想法或解决方案?

你需要多少个数字,需要什么格式,以及你当前的实现在哪种处理器上需要多长时间? - gnasher729
@gnasher729,你好,实际上我有大约200万个顶点,其坐标为xyz浮点数。由于我希望将这些信息输出到obj文件中,因此需要将其转换为字符串(例如:“V”+pos.x+“”...)。目前的to_string实现在低分辨率模型上大约需要3分钟,其中大约有300000个顶点。顺便说一下,我正在旧的第一代i5上运行。 - vincent911001
你使用的编译器是哪个?如果你使用gcc5,可能会有一些性能提升,因为它使用了小字符串优化而不是写时复制。如果我没记错的话,Clang也使用SSO。 - dau_sama
@dau_sama,我正在使用来自Visual Studio 2013 Community的MSVC编译器,谢谢。 - vincent911001
2个回答

19

以下是将浮点数转换为十进制字符串表示的一些最快算法:

撰写本文时,Dragonbox是这些方法中最快的方法,其次是Schubfach,然后是名为Grisu-Exact(不要与Grisu2和Grisu3混淆)的Grisu变体,最后是Ryū:

enter image description here

Dragonbox的实现在这里可用。它也包含在{fmt}库中,集成了一个高级格式化API。为了获得最大的性能,您可以使用带有堆栈分配缓冲区的format_to,例如:

fmt::memory_buffer buf;
fmt::format_to(buf, "{}", 4.2);
// buf.data() returns a pointer to the formatted data & buf.size() gives the size

2
一种优化的方法是不直接使用to_string,因为每次调用它都会创建一个新的字符串。你可能最终会复制该字符串,这不是很高效。
你可以分配一个足够大的char缓冲区来存储所有需要的字符串表示,然后使用printf。

http://www.cplusplus.com/reference/cstdio/printf/

重新使用同一缓冲区。 如果您将浮点数的精度限制为固定数量的小数位,您可以计算浮点数在数组中表示的偏移量。
例如,如果我们只有一个值数组:
index = 1;
float f = value[index];
//corrresponding 6 chars float
const char* s = char_array[index*1];
//the representation will start at position 6, and it will be null terminated so you can use it as a string

为了澄清,您的char_array将如下所示:
1.2000\02.4324\0...

抱歉我的理解能力不太好。从我所理解的来看,char_array[index*1]是一个大缓冲区的分配,所有浮点数的值都必须进入这个缓冲区,我理解的对吗? - vincent911001
嗨,dau_sama,另一个问题是,我们如何确定要分配多大的缓冲区? - vincent911001
确实,您将所有内容存储在同一个缓冲区中。如果您知道有多少个值,就可以知道要分配的缓冲区的大小。如果事先不知道它,这可能有点问题,当达到限制时需要扩展它。这只是变得更加复杂了一些。 - dau_sama
嗨,感谢澄清,我已经成功使用sprintf函数来处理char*缓冲区。然而,我仍然不知道需要分配多大的缓冲区。所以,如果缓冲区达到了极限,我们该如何重新调整缓冲区大小呢?谢谢。 - vincent911001
你知道缓冲区中的位置,也知道下一个值是否会溢出缓冲区。你需要做的是分配(malloc)一个更大的缓冲区,并将旧值复制到新缓冲区中。完成后,你可以释放以前的缓冲区 :-) 还要检查realloc。 - dau_sama

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接