27

我的应用程序需要将双精度值转换为 char* 以写入仅接受字符的管道。执行此操作的常用方法是使用sprintf()函数或使用iomanip.h 头文件中的ostringstream

事实证明,这两者的表现都非常糟糕。而且我的应用程序需要经常进行这种转换,以至于它成为主要的瓶颈。

还有其他我可以使用的功能吗?我可以使用什么逻辑来编写高效的转换函数?到目前为止,我唯一能想到的就是使用除法和 mod 操作来获取每个单独的数字,并将这些数字附加到 char* 以获得整个 double 值。不过,这似乎不是一个好方法,而且本身性能可能会很差。

提前感谢您的想法。

编辑:关于如何使用 char* 存在一些混淆。char* 将是写入管道的 fwrite 函数的参数。

4

9 回答 9

19

如果您想打印 double 类型可以支持的任何数字,请使用那里的任何库来完成这项工作。它可以帮助您保持理智:为什么“dtoa.c”包含这么多代码?

如果您想以 double 类型打印数字的子集。例如,小数点后最多 4 位,小数点前不超过 5 位,则可以将数字四舍五入并转换为 int 类型,然后再使用除法和 mod 打印出来。我可以确认这种方法的性能。


编辑: 如果您最初的目的是发送数据进行通信,那么发送 double 的二进制形式将是最快和最准确的方法(不会因转换而损失精度)。其他答案中解释了这样做的方法。

于 2012-05-25T06:48:20.213 回答
10

您可以将 std::ostreamwrite和 std::istreamread方法用于任何数据类型,您只需将数据重新解释为 char 指针:

double value = 5.0;
std::ostream os;
//...
os.write(reinterpret_cast<const char*>(&value), sizeof(value));
//..
std::istream is;
is.read(reinterpret_cast<char*>(&value), sizeof(value));
于 2012-05-25T06:53:21.157 回答
7

如果您想逐字节读取数据。使用这种技术

double dbl = 2222;
char* ptr = (char*)(&dbl);

这将返回 dbl 的最低字节。ptr++ 将引用 dbl 的第二个字节。

于 2012-05-25T06:45:01.760 回答
4

你能控制管道的两端吗?您只是想通过双打隧道还是需要双打的有效文本表示?

如果您只是在挖隧道并且管道清洁度为 8 位,请使用上述答案之一。

如果您需要字符串,请使用上述其他答案之一。

如果您的问题是管道只有 7 位宽,则在写入时转换为基数 64 ,然后在读取时再次转换为基数。

于 2012-05-25T13:09:00.167 回答
2
/*

_ecvt_s Converts a double number to a string.

Syntax:

errno_t _ecvt_s( 
   char * _Buffer,
   size_t _SizeInBytes,
   double _Value,
   int _Count,
   int *_Dec,
   int *_Sign
);

[out] _Buffer
Filled with the pointer to the string of digits, the result of the conversion.

[in] _SizeInBytes
Size of the buffer in bytes.

[in] _Value
Number to be converted.

[in] _Count
Number of digits stored.

[out] _Dec
Stored decimal-point position.

[out] _Sign
Sign of the converted number.


*/


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

...
char *buf = (char*) malloc(_CVTBUFSIZE);
int decimal;
int sign;
int err;


err = _ecvt_s(buf, _CVTBUFSIZE, 1.2, 5, &decimal, &sign);

if (err != 0) {
// implement error handling
}
else printf("Converted value: %s\n", buf);

...

希望这可以帮助。

于 2012-05-25T12:01:38.433 回答
2

您系统上的缓慢部分sprintf可能不一定是转换双精度,而是解析格式字符串。这对您来说可能是个好消息,因为您可以优化掉它。

此外,努力记录你所拥有的关于double你需要处理的值的范围、准确性和性质的所有知识,并用它来开发一个特殊的算法。

假设您的输入绝不是次正规数,使用已知的固定精度和准确度,相对高性能的结果可能如下所示:

itoa((int)((f + 0.00001) * 10000))

但是,您已经知道的sprintf和方法是唯一完全通用且可移植的解决方案。ostream

于 2012-05-25T07:00:23.047 回答
2

一些系统提供dtostredtostrf转换功能 - 可能值得进行基准测试。您可以查看sprintf()源代码(例如 GNU 版本)以获取想法,根据需要选择%e%f/或%g格式,避免格式字符串解释步骤,甚至使其可内联,并调整性能以适应口味 - 您可能可以删除NaN、infinity 和其他值的特殊大小写,如果您知道不必处理它们。

于 2012-05-25T06:46:47.793 回答
1

使用ftoa会比sprintf它在内部使用的稍微好一点。在此处查看相关问题 另请查看 ftoa 在您的库源代码中的实现方式,看看您是否可以针对您的特定场景对其进行改进。

这似乎ftoa根本不标准,我的错。这是一个讨论,显示了一个声称比 sprintf 快得多的实现。您需要在自己的目标环境中进行分析,并使用 double 而不是 float 来实现。

于 2012-05-25T06:50:43.237 回答
0

关于 dtostre,还有一个扩展版本 dtostren0( https://github.com/63n0m3/Stdlib_extension ),程序员可以决定返回的数字以人类友好的方式显示多少小数位(不带 e+n),正常人会写的方式。它仍然取决于原始 dtostre。披露:我是作家。

于 2021-11-13T07:22:29.940 回答