Convert.ToDouble的更快替代品
是否存在将String转换为Double比Convert.ToDouble 更快的方法?
我监视过System.Convert.ToDouble(String)调用并降低了我的应用程序性能。
Convert.ToDouble("1.34515");
Jeffrey Sax的工作答案:
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];
}
通过使用NumberStyles
和IFormatProvider
特定缓存实例(即CultureInfo
)调用Double.TryParse
,可以节省大约10%:
var style = System.Globalization.NumberStyles.AllowDecimalPoint; var culture = System.Globalization.CultureInfo.InvariantCulture; double.TryParse("1.34515", style, culture, out x);
Convert.ToDouble
和Double.Parse
或Double.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倍。
我无法重现这一点。 此代码测试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
问题的可能性Convert.ToDouble
。
你可以调用double.Parse("1.34515");
这是Convert.ToDouble
包装的内容。
调用double.TryParse
可能会更快,这将避免exception开销。
您可以尝试通过使用Double.Parse(String, NumberStyles, IFormatProvider)
重载来减少对Thread.CurrentCulture
的调用次数。 虽然我怀疑它会产生重大影响。
解析为另一种类型可能会发生: float
或decimal
可能会赢得几个百分点。
有点疯狂,但是……你可以缓存NumberFormatInfo
实例并使用reflection直接调用内部System.Number.ParseDouble
。 这会减少对NumberFormatInfo.GetInstance()
的调用次数,但说实话,我希望reflection速度要慢得多。
剩下的唯一选择(除了避免解析)是使用一些自定义解析方法。 例如,如果您为数字定义了严格的格式(例如#.####
),您可能最终会得到更快,更灵活和/或更安全的实现。 但考虑到内置解析是半原生的,我怀疑你会赢。
UPDATE
我对.NET代码进行了更多的分析,发现NumberFormatInfo
是一个IFormatProvider
。 所以似乎最快的代码应该是:
IFormatProvider _CachedProvider = NumberFormatInfo.CurrentInfo; var value1 = double.Parse(str1, NumberStyles.Number, _CachedProvider); var value2 = double.Parse(str2, NumberStyles.Number, _CachedProvider);
此代码应尽可能减少解析准备所花费的时间。 如果你解析很多字符串对,你也可以将IFormatProvider
缓存提取到一个外部代码(可能)运行一个循环并赢得另外几毫秒。
double.Parse()
应该快一点。
如果您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%,但我不会在任何严肃的应用程序中使用它 – 与正确的解析相比它非常有限,我无法想到你需要解析数百万双打的过程几毫秒就会有所不同。
这篇文章中的函数更快的十进制替代.Parse基于Jeffrey Sax的代码。 它增加了对负数的支持,通过缓存input来优化性能.Length变为变量,也适用于较大的数字。