最快的双字符串转换

时间:2016-09-21 11:23:15

标签: c# .net string performance floating-point

当我编写一个包含数百万double个值的大型CSV文件时,瓶颈似乎是将double转换为字符串。

将{double}值附加到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));

如果我写一个常量字符串而不是一个double,程序的完成速度会快10倍 如果我写一个int而不是double,它仍然是两倍多 (所有测试均在发布模式下执行,未附带调试器)

将此双精度转换为字符串的最快方法是什么?

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

我在一个文件中写了100万个双打,连续100次。

总时间为25.2秒。 只有double.ToString的循环,没有streamwriter.Write在21秒内完成。 只有streamwriter.Write的循环在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);

2 个答案:

答案 0 :(得分:1)

将double转换为字符串是一件很复杂的事情,如果你需要转换很多双打,可能会成为巨大的性能杀手。如果.NET版本对你来说太慢或根本没有转换(并找到解决问题的另一种方法),那么你唯一的选择就是实现更好/更快的转换功能。

为了加快转换速度,您可能需要尝试Grisu,这是Florian Loitsch推出的快速转换算法的C#版本。您需要自己应用4位小数格式,但这可以通过一些简单的字符串操作来完成。

如果您想构建自己的版本(也可以使用您的号码中的特定属性),此java based approach可能会提供一些见解。

答案 1 :(得分:1)

通用的双串转换器必须注意各种边缘情况,如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);
}

或类似的东西......