最快的双到字符串转换

本文关键字:字符串 转换 | 更新日期: 2023-09-27 18:08:09

当我编写一个包含数百万个double值的大型csv类文件时,瓶颈似乎是double到string的转换。

将双精度值附加到StreamWriter的最快方法是什么,在点后固定数字数?

目前我使用

// called once 
System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo();
nfi.NumberDecimalDigits = 4;
// called millions of times in a loop
streamwriter.Write(mydouble.ToString(nfi));

如果我写一个常量字符串而不是双精度类型,程序完成速度会快10倍。
如果我写一个int型而不是double型,它的速度仍然是原来的两倍多。
(所有测试都在发布模式下执行,没有附加调试器)

将这个double类型转换为string类型的最快方法是什么?


我在下面包含了一个基准来说明我的问题:

向文件写入100万个双精度对象,连续写入100次。

总时间为25.2秒。循环只带双份。ToString,没有流写入器。写在21秒内完成。只有流写器的循环。3.5秒内完成写入

System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo();
nfi.NumberDecimalDigits = 4;
double d = 0.1234;
Stopwatch watch;
watch = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
    using (StreamWriter sw = new StreamWriter(@"c:'temp'test.txt", false, Encoding.UTF8, 65536))
    {
        for (int j = 0; j < 1000000; j++)
        {
            sw.Write(d.ToString(nfi));
        }
    }
}
Console.WriteLine("stream.Write & double.ToString: {0}", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
    using (StreamWriter sw = new StreamWriter(@"c:'temp'test.txt", false, Encoding.UTF8, 65536))
    {
        for (int j = 0; j < 1000000; j++)
        {
            sw.Write("0.1234");
        }
    }
}
Console.WriteLine("only stream.Write: {0}", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
    using (StreamWriter sw = new StreamWriter(@"c:'temp'test.txt", false, Encoding.UTF8, 65536))
    {
        for (int j = 0; j < 1000000; j++)
        {
            string s = d.ToString(nfi);
        }
    }
}
Console.WriteLine("only double.ToString: {0}", watch.ElapsedMilliseconds);

最快的双到字符串转换

通用的双-字符串转换器必须注意各种边缘情况,如NaN,超大数,超大数,更不用说要动态计算小数点右侧保留多少位了。

如果你知道数字范围,你可以自己做,把不同的片段转换成整数。例如(在C中):

bool bNegative = false;
if (v < 0){v = -v; bNegative = true;} // make v >= 0
double fv = floor(v); // get integer part as double
int i = (int)fv;      // get integer part as integer
int f = (int)floor((v - fv)*1000.0); // get fraction thousandths as integer
// print the integer and the fractional thousandths, both as integers
if (bNegative){
    fprintf(file, "-%d.%03d", i, f);
} else {
    fprintf(file, "%d.%03d", i, f);
}

将双精度类型转换为字符串是一件复杂的事情,如果需要转换大量双精度类型,可能会造成巨大的性能损失。如果。net版本对你来说太慢了,你唯一的选择就是实现一个更好/更快的转换函数,或者根本不转换(并找到另一种解决问题的方法)。

试试ryya的快速浮点到字符串转换算法,它也有双字符串实现。如果需要控制小数点的个数,最简单的方法是增加一个参数并使用min函数,同时考虑sign是否存在。

ryui比Grisu更快,Grisu是Florian Loitsch引入的快速转换算法的c#版本。您需要自己应用4位小数格式,但这可以通过一些简单的字符串操作来完成。