Convert.ToDouble的更快替代品

时间:2011-12-10 16:20:22

标签: c# performance

是否存在更快方式将 String 转换为 Double 而不是 Convert.ToDouble

我监视过System.Convert.ToDouble(String)调用及其降低我的应用程序性能。

Convert.ToDouble("1.34515");

Perfomance screenshot

杰弗里·萨克斯的工作答案:

static decimal[] decimalPowersOf10 = { 1m, 10m, 100m, 1000m, 10000m, 100000m, 1000000m }; 
static decimal CustomParseDecimal(string input) { 
    long n = 0; 
    int decimalPosition = input.Length; 
    for (int k = 0; k < input.Length; k++) { 
        char c = input[k]; 
        if (c == '.') 
            decimalPosition = k + 1; 
        else 
            n = (n * 10) + (int)(c - '0'); 
    } 
    return n / decimalPowersOf10[input.Length - decimalPosition]; 

}

After Jeffrey Sax CustomParser

7 个答案:

答案 0 :(得分:8)

通过使用Double.TryParseNumberStyles的特定缓存实例IFormatProvider调用CultureInfo可以节省大约10%的费用:

var style = System.Globalization.NumberStyles.AllowDecimalPoint;
var culture = System.Globalization.CultureInfo.InvariantCulture;
double.TryParse("1.34515", style, culture, out x);

Convert.ToDoubleDouble.ParseDouble.TryParse都必须假设输入可以采用任何格式。如果您确定您的输入具有特定格式,则可以编写一个性能更好的自定义解析器。

这是一个转换为decimal的人。转换为double类似。

static decimal CustomParseDecimal(string input) {
    long n = 0;
    int decimalPosition = input.Length;
    for (int k = 0; k < input.Length; k++) {
        char c = input[k];
        if (c == '.')
            decimalPosition = k + 1;
        else
            n = (n * 10) + (int)(c - '0');
    }
    return new decimal((int)n, (int)(n >> 32), 0, false, (byte)(input.Length - decimalPosition));
}

我的基准测试表明,这比decimal的原始速度提高了约5倍,如果使用整数,则高达12倍。

答案 1 :(得分:3)

我无法重现这一点。此代码测试Convert.ToDouble的速度。

        int numTests = 10000;
        double sum = 0;
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < numTests; ++i)
        {
            var d = Convert.ToDouble("1.23456");
            sum += d;
        }
        sw.Stop();
        Console.WriteLine("{0} tests @ {1} ms. Avg of {2:N4} ms each", numTests,
           sw.ElapsedMilliseconds, (double)sw.ElapsedMilliseconds/numTests);
        Console.WriteLine("sum = {0}", sum);

有10,000个电话,我

10000 tests @ 3 ms. Avg of 0.0003 ms each
sum = 12345.6000000021

处于发布模式,没有附带调试器。

问题出在Convert.ToDouble的可能性极小。

答案 2 :(得分:2)

你可以拨打double.Parse("1.34515"); Convert.ToDouble包裹的double.TryParse

调用{{1}}可能会更快,这将避免异常开销。

答案 3 :(得分:1)

您可以尝试使用Thread.CurrentCulture overload减少对Double.Parse(String, NumberStyles, IFormatProvider)的来电次数。虽然我怀疑它会产生重大影响。

可能会解析为其他类型:floatdecimal可能会赢得几个百分点。

有点疯狂,但是......你可以缓存NumberFormatInfo实例并使用反射来直接调用内部System.Number.ParseDouble。这会减少对NumberFormatInfo.GetInstance()的调用次数,但说实话,我希望反射速度要慢得多。

剩下的唯一选择(除了避免解析)是使用一些自定义解析方法。例如,如果您为数字定义严格格式(例如#.####),您可能最终会得到更快,但不太灵活和/或安全的实现。但考虑到内置解析是半原生的,我怀疑你会赢。

<强>更新

我对.NET代码进行了更多分析,发现NumberFormatInfoIFormatProvider。所以似乎最快的代码应该是:

IFormatProvider _CachedProvider = NumberFormatInfo.CurrentInfo;
var value1 = double.Parse(str1, NumberStyles.Number, _CachedProvider);
var value2 = double.Parse(str2, NumberStyles.Number, _CachedProvider);

此代码应尽可能减少解析准备所花费的时间。如果你解析了很多字符串对,你也可以将IFormatProvider缓存提取到一个外部代码,该外部代码可能会运行一个循环并赢得另外几毫秒。

答案 4 :(得分:1)

此帖Faster alternative to decimal.Parse中的功能基于Jeffrey Sax的代码。它增加了对负数的支持,通过缓存input来优化性能.Length变为变量,也适用于更大的数字。

答案 5 :(得分:0)

double.Parse()应该快一点。

答案 6 :(得分:0)

如果您100%确定源数据格式范围,可以使用:

string num = "1.34515";
int len = num.Length - num.IndexOf('.') - 1;
int intval = Int32.Parse(num.Replace(".", ""));
double d = (double)intval / PowersOf10[len]; // PowersOf10 is pre-computed or inlined

对我来说,它比Double.Parse快了大约50%,但我不会在任何严肃的应用程序中使用它 - 与正确的解析相比,它是非常有限的,我无法想到你需要解析的进程数百万的双打和几毫秒就会有所不同。