I noticed decimal.Parse(number, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture)
is about 100% slower than custom decimal parse method based on Jeffrey Sax's code from Faster alternative to Convert.ToDouble
public static decimal ParseDecimal(string input) {
bool negative = false;
long n = 0;
int len = input.Length;
int decimalPosition = len;
if (len != 0) {
int start = 0;
if (input[0] == '-') {
negative = true;
start = 1;
}
for (int k = start; k < len; 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, negative, (byte)(len -decimalPosition));
}
I assume that is because native decimal.Parse
is designed to struggle with number style and culture info.
However, above mentioned method doesn't use 3rd parameter hi byte in new decimal
so it won't work with larger numbers.
Is there a faster alternative to decimal.Parse
to convert string that consists only of numbers and decimal dot to decimal which would work with large numbers?
EDIT: Benchmark:
var style = System.Globalization.NumberStyles.AllowDecimalPoint;
var culture = System.Globalization.CultureInfo.InvariantCulture;
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
s.Reset();
s.Start();
for (int i=0; i<10000000; i++)
{
decimal.Parse("20000.0011223344556", style, culture);
}
s.Stop();
Console.WriteLine(s.Elapsed.ToString());
s.Reset();
s.Start();
for (int i=0; i<10000000; i++)
{
ParseDecimal("20000.0011223344556");
}
s.Stop();
Console.WriteLine(s.Elapsed.ToString());
output:
00:00:04.2313728
00:00:01.4464048
Custom ParseDecimal is in this case significantly faster than decimal.Parse.
Thanks for all your comments which gave me a little more insight. Finally I did it as follows. If input is too long then it separates input string and parses first part using long and the rest with int which is still faster than decimal.Parse.
This is my final production code:
public static int[] powof10 = new int[10]
{
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000
};
public static decimal ParseDecimal(string input)
{
int len = input.Length;
if (len != 0)
{
bool negative = false;
long n = 0;
int start = 0;
if (input[0] == '-')
{
negative = true;
start = 1;
}
if (len <= 19)
{
int decpos = len;
for (int k = start; k < len; k++)
{
char c = input[k];
if (c == '.')
{
decpos = k +1;
}else{
n = (n *10) +(int)(c -'0');
}
}
return new decimal((int)n, (int)(n >> 32), 0, negative, (byte)(len -decpos));
}else{
if (len > 28)
{
len = 28;
}
int decpos = len;
for (int k = start; k < 19; k++)
{
char c = input[k];
if (c == '.')
{
decpos = k +1;
}else{
n = (n *10) +(int)(c -'0');
}
}
int n2 = 0;
bool secondhalfdec = false;
for (int k = 19; k < len; k++)
{
char c = input[k];
if (c == '.')
{
decpos = k +1;
secondhalfdec = true;
}else{
n2 = (n2 *10) +(int)(c -'0');
}
}
byte decimalPosition = (byte)(len -decpos);
return new decimal((int)n, (int)(n >> 32), 0, negative, decimalPosition) *powof10[len -(!secondhalfdec ? 19 : 20)] +new decimal(n2, 0, 0, negative, decimalPosition);
}
}
return 0;
}
benchmark code:
const string input = "[inputs are below]";
var style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign;
var culture = System.Globalization.CultureInfo.InvariantCulture;
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
s.Reset();
s.Start();
for (int i=0; i<10000000; i++)
{
decimal.Parse(input, style, culture);
}
s.Stop();
Console.WriteLine(s.Elapsed.ToString());
s.Reset();
s.Start();
for (int i=0; i<10000000; i++)
{
ParseDecimal(input);
}
s.Stop();
Console.WriteLine(s.Elapsed.ToString());
results on my i7 920:
input: 123.456789
00:00:02.7292447
00:00:00.6043730
input: 999999999999999123.456789
00:00:05.3094786
00:00:01.9702198
input: 1.0
00:00:01.4212123
00:00:00.2378833
input: 0
00:00:01.1083770
00:00:00.1899732
input: -3.3333333333333333333333333333333
00:00:06.2043707
00:00:02.0373628
If input consists only of 0-9, . and optionally - at the begining then this custom function is significantly faster for parsing string to decimal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With