Double tryparse return value is inconsistent - c#

I have a strange problem. When my custom formatted Hex value is tested with double.Tryparse it returns inconsistent results
For example
if (double.TryParse(dblValue, NumberStyles.Float, CultureInfo.InvariantCulture, out double x))
If the dblValue = "0E-63" the above statement returns true
and if dblValue = "0E-5B" it returns false
I expect both of these custom formatted hex values to return false in TryParse, so that my code handles them as not a number.
What am I missing here.
other than this double and integers are working as expected.

0E-63 here is not hex - it is 0 x (10 to the power of -63) - in the same way that 1E-03 parses as 0.001 i.e. 1 x (10 to the power of -3).
This API does not parse hex.

Related

String to Decimal with 2 decimal places always

I have a string that is to be converted to decimal. The string could be entered with no decimal places e.g. "123" or 2 decimal places, e.g. "123.45" or somewhat awkwardly, 1 decimal place "123.3". I want the number displayed (the Property invoice.Amount which is type decimal) with 2 decimal places. The code below does that. I think it could be written better though. How?
decimal newDecimal;
bool isDecimal = Decimal.TryParse(InvoiceDialog.InvoiceAmount, out newDecimal);
string twoDecimalPlaces = newDecimal.ToString("########.00");
invoice.Amount = Convert.ToDecimal(twoDecimalPlaces);
In part, I don't understand, for the string formatting "########.00", what # does and what 0 does. E.g. how would it be different if it were "########.##"?
# is an optional digit when 0 is a mandatory digit
For instance
decimal d = 12.3M;
// d with exactly 2 digits after decimal point
Console.WriteLine(d.ToString("########.00"));
// d with at most 2 digits after decimal point
Console.WriteLine(d.ToString("########.##"));
Outcome:
12.30 // exactly 2 digits after decimal point: fractional part padded by 0
12.3 // at most 2 digits after decimal point: one digit - 3 - is enough
Basically, # means optional, where as 0 is mandatory.
As for better explanation, if you put # then if number is available to fullfil the placeholder it'll be added if not it'll be ignored.
Putting 0 however is different as it'll always put a value in for you.
You can combine the two together.
String.Format("{0:0.##}", 222.222222); // 222.22
String.Format("{0:0.##}", 222.2); // 222.2
String.Format("{0:0.0#}", 222.2) // 222.2
The "#" is optional while the "0" will show either the number or 0.
For example,
var x = 5.67;
x.ToString("##.###"); // 5.67
x.ToString("00.000"); // 05.670
x.ToString("##.##0"); // 5.670
If you just care about how many decimal places you have, I would recommend using
x.ToString("f2"); // 5.67
to get 2 decimal spots.
More information can be found at http://www.independent-software.com/net-string-formatting-in-csharp-cheat-sheet.html/.
You don't need to convert the decimal to string to do the formatting for 2 decimal places. You can use the decimal.Round method directly. You can read about it here.
So your code can be converted to
decimal newDecimal;
Decimal.TryParse(s, out newDecimal);
newDecimal = decimal.Round(newDecimal, 2, MidpointRounding.AwayFromZero);
The above code also be simplified with C# 7.0 declaration expression as
Decimal.TryParse(s, out decimal newDecimal);
newDecimal = decimal.Round(newDecimal, 2, MidpointRounding.AwayFromZero);
Now newDecimal will have have a value with 2 precision.
You can check this live fiddle.

decimal rounding to 2 precision such that X is converted to X.00 C# [duplicate]

This question already has answers here:
How do I display a decimal value to 2 decimal places?
(19 answers)
Closed 7 years ago.
First of all this is not a duplicate question my need is to have 2 precision decimal always
e.g.
2 should be converted as 2.00
0 should be converted as 0.00
0.5 should be converted as 0.50
output number has to be decimal
Things i had tried
decimal val = 0.5000M
decimal d = Math.Round(val, 2);//gives 0.5 i need 0.50
NOTE: I am not looking to convert decimal to string as i need to send it as a soap request which accepts decimal value only, so i dont have flexibility to convert it to string.
Update
below code as answered by Jakub Lortz works fine though it may look complex
solution 1 by Jakub Lortz
decimal twoPoint00 = new Decimal(200, 0, 0, false, 2);
decimal twoPoint0 = new Decimal(20, 0, 0, false, 1);
Console.WriteLine(twoPoint00) // prints 2.00
Console.WriteLine(twoPoint0) // prints 2.0
solution 2 by Bogdan
he had given very simple solution which will also work
decimal val = 0.5000M;
string result = string.Format("{0:f2}", val);
decimal d;
Decimal.TryParse(result, out d);
There is a constructor of Decimal that allows you to specify the scale:
public Decimal(
int lo,
int mid,
int hi,
bool isNegative,
byte scale
)
For example:
decimal twoPoint00 = new Decimal(200, 0, 0, false, 2);
decimal twoPoint0 = new Decimal(20, 0, 0, false, 1);
Console.WriteLine(twoPoint00) // prints 2.00
Console.WriteLine(twoPoint0) // prints 2.0
This constructor is used by the C# compiler to create decimals from literals, so you can easily check it in the compiled IL.
Edit
To convert a decimal value to a representation with a set precision, you would first have to use Decimal.GetBits method to get its internal representation. Then modify the values (maybe using BigInteger to work with the whole 96-bits value as a whole?) and create a new decimal.
Seems like a lot of job, with a lot of possible bugs. I'd start by checking the solution proposed in Bogdan's answer - ToString() + Parse(). It seems to work correctly and is very simple.
I guess you want to convert the decimal into a string. Try it like that:
string result = d.ToString("#.00");
decimal val = 0.5000M;
string result = string.Format("{0:f2}", val);
decimal d;
Decimal.TryParse(result, out d);
#Jakub Lortz - Thank you for the suggestions:
decimal val = 0.5000M;
string formattedVal = val.ToString("F2");
decimal result = Decimal.Parse(formattedVal);

Arithmetic addition and subtraction on large hexadecimal strings

How the arithmetic addition and subtraction can be carried out on large strings. For example, I have the following hexadecimal strings
string a1="B91EFEBFBDBDBFEFF39ABEE";
string a2="000FFFFFFFFFFFFFFFFFEEE";
then I want to do arithmetic addition a1+a2 to get the sum, not string concatenation.
And then arithmetic subtraction e.g. sum-a2 to get back string a1.
I tried to do
Int64 parseda1 = Int64.Parse(a1);
Int64 parseda2 = Int64.Parse(a2);
Int64 xyz = abc + abc;
MessageBox.Show(xyz.ToString("X"));// may be error in this as well
It trows exception, Input string was not in a correct format.
If you want really large numbers, you can use the BigInteger struct which represents an arbitrarily large signed integer. Try this:
string a1 = "B91EFEBFBDBDBFEFF39ABEE";
string a2 = "000FFFFFFFFFFFFFFFFFEEE";
BigInteger num1 = BigInteger.Parse(a1, NumberStyles.HexNumber);
BigInteger num2 = BigInteger.Parse(a2, NumberStyles.HexNumber);
BigInteger sum = num1 + num2;
Console.WriteLine(sum.ToString("X"));
Console.WriteLine((sum - num2).ToString("X")); //gets a1
Edit:
Looks like num1 gives us a negative number. That's probably not what you want. To fix that, read: MSDN: BigInteger.Parse Method
"If value is a hexadecimal string, the Parse(String, NumberStyles)
method interprets value as a negative number stored by using two's
complement representation if its first two hexadecimal digits are
greater than or equal to 0x80. In other words, the method interprets
the highest-order bit of the first byte in value as the sign bit. To
make sure that a hexadecimal string is correctly interpreted as a
positive number, the first digit in value must have a value of zero.
For example, the method interprets 0x80 as a negative value, but it
interprets either 0x080 or 0x0080 as a positive value."

System.Double value with max characters

I'm testing net xml serialization of double[] arrays so I'm interested to know whats the double value that has most characters int it's serialized for so I can test whats the max output size of serialized array.
It should be 24.
double.MinValue.ToString("R").Length
From double.ToString(string)
or "R", which returns 15 digits if the number can be represented with that precision or 17 digits if the number can only be represented with maximum precision.
you have that there are at max 17 digits, plus 1 for sign, plus 1 for the decimal separator, plus 5 for the E+xxx (double.MaxValue is 1.7976931348623157E+308 and double.Epsilon, the smallest value > 0, is 4.94065645841247E-324, so both in the form E[+-][0-9]{1,3}).
Note that technically, in some strange languages,
var str2 = double.PositiveInfinity.ToString("R");
could be longer (because the string is localized), but I hope you'll serialize your numbers with CultureInfo.InvariantCulture!
But remember that users could have changed their culture from the control panel... something like:
var culture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
culture.NumberFormat.NegativeSign = "Negative";
culture.NumberFormat.NumberDecimalSeparator = "DecimalSeparator";
var str4 = double.MinValue.ToString("R", culture);
Result: Negative1DecimalSeparator7976931348623157E+308
For this reason it's better to use the CultureInfo.InvariantCulture :-)
But if you want to know the truth, in the Control Panel the decimal separator can be long up to 3 characters, and the negative sign up to 4 (you can try it, or you can check the LOCALE_SDECIMAL and LOCALE_SNEGATIVESIGN, clearly the terminating null character can be ignored in .NET)
You can try -1.0 / 3.0, it will have many decimals.

Why does .NET decimal.ToString(string) round away from zero, apparently inconsistent with the language spec?

I see that, in C#, rounding a decimal, by default, uses MidpointRounding.ToEven. This is expected, and is what the C# spec dictates. However, given the following:
A decimal dVal
A format string sFmt that, when passed in to dVal.ToString(sFmt), will result in a string containing a rounded version of dVal
...it is apparent that decimal.ToString(string) returns a value rounded using MidpointRounding.AwayFromZero. This would appear to be a direct contradiction of the C# spec.
My question is this: is there a good reason this is the case? Or is this just an inconsistency in the language?
Below, for reference, I've included some code that writes to console an assortment of rounding operation results and decimal.ToString(string) operation results, each on every value in an array of decimal values. The actual outputs are embedded. After that, I've included a relevant paragraph from the C# Language Specification section on the decimal type.
The example code:
static void Main(string[] args)
{
decimal[] dArr = new decimal[] { 12.345m, 12.355m };
OutputBaseValues(dArr);
// Base values:
// d[0] = 12.345
// d[1] = 12.355
OutputRoundedValues(dArr);
// Rounding with default MidpointRounding:
// Math.Round(12.345, 2) => 12.34
// Math.Round(12.355, 2) => 12.36
// decimal.Round(12.345, 2) => 12.34
// decimal.Round(12.355, 2) => 12.36
OutputRoundedValues(dArr, MidpointRounding.ToEven);
// Rounding with mr = MidpointRounding.ToEven:
// Math.Round(12.345, 2, mr) => 12.34
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.34
// decimal.Round(12.355, 2, mr) => 12.36
OutputRoundedValues(dArr, MidpointRounding.AwayFromZero);
// Rounding with mr = MidpointRounding.AwayFromZero:
// Math.Round(12.345, 2, mr) => 12.35
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.35
// decimal.Round(12.355, 2, mr) => 12.36
OutputToStringFormatted(dArr, "N2");
// decimal.ToString("N2"):
// 12.345.ToString("N2") => 12.35
// 12.355.ToString("N2") => 12.36
OutputToStringFormatted(dArr, "F2");
// decimal.ToString("F2"):
// 12.345.ToString("F2") => 12.35
// 12.355.ToString("F2") => 12.36
OutputToStringFormatted(dArr, "###.##");
// decimal.ToString("###.##"):
// 12.345.ToString("###.##") => 12.35
// 12.355.ToString("###.##") => 12.36
Console.ReadKey();
}
private static void OutputBaseValues(decimal[] dArr)
{
Console.WriteLine("Base values:");
for (int i = 0; i < dArr.Length; i++) Console.WriteLine("d[{0}] = {1}", i, dArr[i]);
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr)
{
Console.WriteLine("Rounding with default MidpointRounding:");
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2) => {1}", d, Math.Round(d, 2));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2) => {1}", d, decimal.Round(d, 2));
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr, MidpointRounding mr)
{
Console.WriteLine("Rounding with mr = MidpointRounding.{0}:", mr);
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2, mr) => {1}", d, Math.Round(d, 2, mr));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2, mr) => {1}", d, decimal.Round(d, 2, mr));
Console.WriteLine();
}
private static void OutputToStringFormatted(decimal[] dArr, string format)
{
Console.WriteLine("decimal.ToString(\"{0}\"):", format);
foreach (decimal d in dArr) Console.WriteLine("{0}.ToString(\"{1}\") => {2}", d, format, d.ToString(format));
Console.WriteLine();
}
The paragraph from section 4.1.7 of the C# Language Specification ("The decimal type") (get the full spec here (.doc)):
The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as “banker’s rounding”). A zero result always has a sign of 0 and a scale of 0.
It's easy to see that they may not have been considering ToString(string) in this paragraph, but I'm inclined to think it fits in this description.
If you read the spec carefully, you will see that there is no inconsistency here.
Here's that paragraph again, with the important parts highlighted:
The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as “banker’s rounding”). A zero result always has a sign of 0 and a scale of 0.
This part of the spec applies to arithmetic operations on decimal; string formatting is not one of those, and even if it were, it wouldn't matter because your examples are low-precision.
To demonstrate the behaviour referred to in the spec, use the following code:
Decimal d1 = 0.00000000000000000000000000090m;
Decimal d2 = 0.00000000000000000000000000110m;
// Prints: 0.0000000000000000000000000004 (rounds down)
Console.WriteLine(d1 / 2);
// Prints: 0.0000000000000000000000000006 (rounds up)
Console.WriteLine(d2 / 2);
That's all the spec is talking about. If the result of some calculation would exceed the precision limit of the decimal type (29 digits), banker's rounding is used to determine what the result will be.
ToString() by default formats according to the Culture, not according to a computational aspect of the specification. Apparently the Culture for your locale (and most, from the looks of it) expects rounding away from zero.
If you want different behavior, you can pass an IFormatProvider in to ToString()
I thought the above, but you are correct that it always rounds away from zero no matter the Culture.
As also linked by a comment on this answer, here (MS Docs) is official documentation on the behavior. Excerpting from the top of that linked page, and focusing on the last two list items:
Standard numeric format strings are used to format common numeric types. A standard numeric format string takes the form Axx, where:
A is a single alphabetic character called the format specifier. Any numeric format string that contains more than one alphabetic character, including white space, is interpreted as a custom numeric format string. For more information, see Custom Numeric Format Strings.
xx is an optional integer called the precision specifier. The precision specifier ranges from 0 to 99 and affects the number of digits in the result. Note that the precision specifier controls the number of digits in the string representation of a number. It does not round the number itself. To perform a rounding operation, use the Math.Ceiling, Math.Floor, or Math.Round method.
When precision specifier controls the number of fractional digits in the result string, the result string reflects a number that is rounded to a representable result nearest to the infinitely precise result. If there are two equally near representable results:
On the .NET Framework and .NET Core up to .NET Core 2.0, the runtime selects the result with the greater least significant digit (that is, using MidpointRounding.AwayFromZero).
On .NET Core 2.1 and later, the runtime selects the result with an even least significant digit (that is, using MidpointRounding.ToEven).
As far as your question ---
Is there a good reason this is the case? Or is this just an inconsistency in the language?
--- the answer implied by the change in behavior from Framework to Core 2.1+ is possibly, "No, there was no good reason, so we (Microsoft) went ahead and made the runtime consistent with the language in .NET Core 2.1 and later."
Most likely because this is the standard way of dealing with currency. The impetus for the creation of decimal was that floating point does a poor job of dealing with currency values, so you would expect it's rules to be more aligned with accounting standards than mathematical correctness.

Categories