Wednesday, October 10, 2007

Wrong result on float.TryParse() method

One of the introduced features in .NET 2.0 platform was TryParse() method, very practical and performance wise extension. TryParse method returns a boolean to denote whether the conversion has been successful or not, and returns the converted value through an out parameter.
In this test case I will try to present possible bug in converting string value to float by using this method.


float f;
string
stringValue = "123,456789101112";
float.TryParse(stringValue, out f);

String value that needs to be converted to float is 123,456789101112, expected float value after parsing would be the same number, but number 123.456787 was returned. Sixth decimal was wrongly rounded / generated, and instead of 9 - number 7 is placed.
Is this conversion culture specific, is some hidden mechanism for rounding applied?

Decimal method for parsing string into decimal value is working correctly.

4 comments:

Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...

Navistina strasen bag. Kolku problemi moze da proizlezat od pogresno zaokruzuvanje na 12 decimala
Pa dali se normalni lugjeto od Microsoft koga mozele tolku seriozen bag da pustat ??? Strasno. Nemam drug komentar

Anonymous said...

Geek much?

private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
string currencyDecimalSeparator;
string currencyGroupSeparator;
char* chPtr2;
number.scale = 0;
number.sign = false;
string currencySymbol = null;
string ansiCurrencySymbol = null;
string numberDecimalSeparator = null;
string numberGroupSeparator = null;
bool flag = false;
if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None)
{
currencySymbol = numfmt.CurrencySymbol;
if (numfmt.ansiCurrencySymbol != null)
{
ansiCurrencySymbol = numfmt.ansiCurrencySymbol;
}
numberDecimalSeparator = numfmt.NumberDecimalSeparator;
numberGroupSeparator = numfmt.NumberGroupSeparator;
currencyDecimalSeparator = numfmt.CurrencyDecimalSeparator;
currencyGroupSeparator = numfmt.CurrencyGroupSeparator;
flag = true;
}
else
{
currencyDecimalSeparator = numfmt.NumberDecimalSeparator;
currencyGroupSeparator = numfmt.NumberGroupSeparator;
}
int num = 0;
bool flag2 = false;
char* p = str;
char ch = p[0];
while (true)
{
if ((!IsWhite(ch) || ((options & NumberStyles.AllowLeadingWhite) == NumberStyles.None)) || (((num & 1) != 0) && (((num & 1) == 0) || (((num & 0x20) == 0) && (numfmt.numberNegativePattern != 2)))))
{
if ((flag2 = ((options & NumberStyles.AllowLeadingSign) != NumberStyles.None) && ((num & 1) == 0)) && ((chPtr2 = MatchChars(p, numfmt.positiveSign)) != null))
{
num |= 1;
p = chPtr2 - 1;
}
else if (flag2 && ((chPtr2 = MatchChars(p, numfmt.negativeSign)) != null))
{
num |= 1;
number.sign = true;
p = chPtr2 - 1;
}
else if (((ch == '(') && ((options & NumberStyles.AllowParentheses) != NumberStyles.None)) && ((num & 1) == 0))
{
num |= 3;
number.sign = true;
}
else
{
if (((currencySymbol == null) || ((chPtr2 = MatchChars(p, currencySymbol)) == null)) && ((ansiCurrencySymbol == null) || ((chPtr2 = MatchChars(p, ansiCurrencySymbol)) == null)))
{
break;
}
num |= 0x20;
currencySymbol = null;
ansiCurrencySymbol = null;
p = chPtr2 - 1;
}
}
ch = *(++p);
}
int num2 = 0;
int index = 0;
while (true)
{
if (((ch >= '0') && (ch <= '9')) || (((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None) && (((ch >= 'a') && (ch <= 'f')) || ((ch >= 'A') && (ch <= 'F')))))
{
num |= 4;
if ((ch != '0') || ((num & 8) != 0))
{
if (num2 < 50)
{
number.digits[num2++] = ch;
if ((ch != '0') || parseDecimal)
{
index = num2;
}
}
if ((num & 0x10) == 0)
{
number.scale++;
}
num |= 8;
}
else if ((num & 0x10) != 0)
{
number.scale--;
}
}
else if ((((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None) && ((num & 0x10) == 0)) && (((chPtr2 = MatchChars(p, currencyDecimalSeparator)) != null) || ((flag && ((num & 0x20) == 0)) && ((chPtr2 = MatchChars(p, numberDecimalSeparator)) != null))))
{
num |= 0x10;
p = chPtr2 - 1;
}
else
{
if (((((options & NumberStyles.AllowThousands) == NumberStyles.None) || ((num & 4) == 0)) || ((num & 0x10) != 0)) || (((chPtr2 = MatchChars(p, currencyGroupSeparator)) == null) && ((!flag || ((num & 0x20) != 0)) || ((chPtr2 = MatchChars(p, numberGroupSeparator)) == null))))
{
break;
}
p = chPtr2 - 1;
}
ch = *(++p);
}
bool flag3 = false;
number.precision = index;
number.digits[index] = '\0';
if ((num & 4) != 0)
{
if (((ch == 'E') || (ch == 'e')) && ((options & NumberStyles.AllowExponent) != NumberStyles.None))
{
char* chPtr3 = p;
ch = *(++p);
chPtr2 = MatchChars(p, numfmt.positiveSign);
if (chPtr2 != null)
{
ch = *(p = chPtr2);
}
else
{
chPtr2 = MatchChars(p, numfmt.negativeSign);
if (chPtr2 != null)
{
ch = *(p = chPtr2);
flag3 = true;
}
}
if ((ch >= '0') && (ch <= '9'))
{
int num4 = 0;
do
{
num4 = (num4 * 10) + (ch - '0');
ch = *(++p);
if (num4 > 0x3e8)
{
num4 = 0x270f;
while ((ch >= '0') && (ch <= '9'))
{
ch = *(++p);
}
}
}
while ((ch >= '0') && (ch <= '9'));
if (flag3)
{
num4 = -num4;
}
number.scale += num4;
}
else
{
p = chPtr3;
ch = p[0];
}
}
while (true)
{
if (!IsWhite(ch) || ((options & NumberStyles.AllowTrailingWhite) == NumberStyles.None))
{
if ((flag2 = ((options & NumberStyles.AllowTrailingSign) != NumberStyles.None) && ((num & 1) == 0)) && ((chPtr2 = MatchChars(p, numfmt.positiveSign)) != null))
{
num |= 1;
p = chPtr2 - 1;
}
else if (flag2 && ((chPtr2 = MatchChars(p, numfmt.negativeSign)) != null))
{
num |= 1;
number.sign = true;
p = chPtr2 - 1;
}
else if ((ch == ')') && ((num & 2) != 0))
{
num &= -3;
}
else
{
if (((currencySymbol == null) || ((chPtr2 = MatchChars(p, currencySymbol)) == null)) && ((ansiCurrencySymbol == null) || ((chPtr2 = MatchChars(p, ansiCurrencySymbol)) == null)))
{
break;
}
currencySymbol = null;
ansiCurrencySymbol = null;
p = chPtr2 - 1;
}
}
ch = *(++p);
}
if ((num & 2) == 0)
{
if ((num & 8) == 0)
{
if (!parseDecimal)
{
number.scale = 0;
}
if ((num & 0x10) == 0)
{
number.sign = false;
}
}
str = p;
return true;
}
}
str = p;
return false;
}