The condition Double.TryParse(node.ChildNodes[4].InnerText, out course) doesn't work. It's always return 0.
foreach (XmlNode node in XmlDoc.SelectNodes("/ValCurs/Valute"))
{
if (node.ChildNodes[1].InnerText == curr_name) // CharCode
{
// throw new Exception(node.ChildNodes[4].InnerText); //returns 2.085 but the next condition doesn't work
if (Double.TryParse(node.ChildNodes[4].InnerText, out course)) // course value, nominal = 100
{
if (Int32.Parse(node.ChildNodes[2].InnerText) == 10) course *= 10; //for RUB, BYR nominal = 10
if (Int32.Parse(node.ChildNodes[2].InnerText) == 1000) course /= 10; //for JPY, HUF nominal = 1000
return course;
}
}
XML fragment
<Valute id="1">
<NumCode>036</NumCode>
<CharCode>AUD</CharCode>
<Nominal>100</Nominal>
<Name>Australian dollars</Name>
<Value>1276.6300</Value>
</Valute>
TryParse method uses the current culture thus the number formats might differ depending on you system.
You can use the override version of TryParse where you pass IFormatProvider as a parameter. CultureInfo.InvariantCulture uses . as a decimal separator:
Double.TryParse(node.ChildNodes[4].InnerText, CultureInfo.InvariantCulture, out course)
Btw. checking values by throwing them as exceptions is rather interesting approach, but I highly recommend debugging/logging to check what is happening.
Related
code(from interactive shell):
> var a = new Dictionary<float, string>();
> a.Add(float.NaN, "it is NaN");
> a[float.NaN]
"it is NaN"
So it is possible, but is it safe?
Paraphrasing from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Single.cs;
public const float NaN = (float)0.0 / (float)0.0;
public static unsafe bool IsNaN(float f) => f != f;
public int CompareTo(object? value){
...
if (m_value < f) return -1;
if (m_value > f) return 1;
if (m_value == f) return 0;
if (IsNaN(m_value))
return IsNaN(f) ? 0 : -1;
else // f is NaN.
return 1;
}
public bool Equals(float obj)
{
if (obj == m_value)
{
return true;
}
return IsNaN(obj) && IsNaN(m_value);
}
public override int GetHashCode()
{
int bits = Unsafe.As<float, int>(ref Unsafe.AsRef(in m_value));
// Optimized check for IsNan() || IsZero()
if (((bits - 1) & 0x7FFFFFFF) >= 0x7F800000)
{
// Ensure that all NaNs and both zeros have the same hash code
bits &= 0x7F800000;
}
return bits;
}
You can see that NaN requires special handling in each of these cases. The standard IEEE representation leaves most bits undefined, and defines special cases for comparisons even if those bit values are identical.
However you can also see that both GetHashCode() && Equals() treat two NaN's as equivalent. So I believe that using NaN as a dictionary key should be fine.
That depends on what you mean by safe.
If you expect people to be able to use the dictionary and compare its keys to other floats, they will have to deal with a key value of NaN correctly themselves. And since float.NaN == float.NaN happens to be False, that may cause issues down the line.
However, the Dictionary succeeds in performing the lookup and other operations work correctly as well.
The question here is really why you need it in the first place?
It's bad idea to use float as key of Dictionary.
In theory you can do it. But when you work with float\double\decimal you shoud use some Epsilon to compare 2 values. Use formula like this:
abs(a1 - a2) < Epsilon
It's need due to rounding of float in operations and existing of irrational numbers. For example how you will compare with PI or sqrt(2)?
So, on this case using float as dictionary key is bad idea.
for example
double x = -1;
TrySetX(ref x);
if(x == -1)
//since x == -1, it obviously wasn't set
TryADifferentWayToSetX(ref x);
Use(x);
in the case where x is not changed, will x == -1 always return true, or will I have to use an epsiolon for the comparison?
The logic here is that both literals will presumable be converted to the same value, so there is no need to worry about the fact that they may lose precision.
Trying it out, it looks like both literals get converted to the same value. As such the comparison is safe:
Here is a little test I wrote
public class Program
{
public static void Main(string[] args)
{
double x = 3.455678756567656765677812345678912345678901234567890123456789009876543223456798765423456709876512345;
System.Console.WriteLine(x);
System.Console.WriteLine(x == 3.455678756567656765677812345678912345678901234567890123456789009876543223456798765423456709876512345);
}
}
That number is long enough there will be a large loss in precision, but it prints
3.45567875656766
True
So indeed there is loss of precision, but the comparison nonetheless works.
I have read the second line in a text file which contains 2.75 and I am trying to get it to do something if it meets certain criteria. I'm sure I have done this before and has a simple answer but I can't seen to figure it out.
string SecondLine;
using (var reader = new StreamReader(SPFile2))
{
reader.ReadLine();
SecondLine = reader.ReadLine();
}
int NewValue;
NewValue = Convert.ToInt32(SecondLine);
if ((NewValue >= 2) && (NewValue <= 2.99))
{
// Do Something
}
if ((NewValue >= 3) && (NewValue <= 3.99))
{
// Do something else
}
What have I missed out?
You are converting a decimal number to an Int32 that does not hold decimals. This will turn the number in NewValue into 2 as it truncates towards zero. You need to store the variable in a double, float or decimal whichever is best for your requirements.
See the following example that uses a double and Parse:
double newValue = Double.Parse(secondLine);
Note that if you are unsure if the value will be a double you should use Double.TryParse
double newValue;
bool result = Double.TryParse(secondLine, out newValue);
if (!result) //Parse failed
Note that if the parsing fails it may be down to your culture settings i.e. a ',' for the decimal separator not a '.'. However there is an overload for Parse and TryParse that allows you to pass culture information in.
You are trying to parse a string that represents a double into a integer
that will cause a
System.IFormatException
{"Input string was not in a correct
format."}{"Input string was not in a correct format."}
if you know it is a number with decimal part then do:
var newValue = Convert.ToDouble(secondLine);
if you know it is an integer then try:
var newValue = Convert.ToInt32(secondLine);
I am seeing lots of these in a method in our code:
int num1 = 0;
if (Char.IsDigit(myStr[2]) && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{
valid = false;
}
So are they just making sure the third character us a digit?
The code shown parses the 3rd character only - checking if it is digit, then parsing the string representation of that single character. Instead, just use the numeric value of that character:
if(myStr[2] >= '0' && myStr[2] <= '9') {
num1 = (int)myStr[2] - (int)'0';
} else {
valid = false
}
You can safely skip the IsDigit() check as it's redundant.
TryParse() will fail if it's not a digit.
As it has been pointed out by others, Char.IsDigit() is quicker. If your code is performance sensitive the check makes sense.
If you leave the IsDigit check in place, then you can reduce TryParse to Int32.Parse() as at that point the parsing won't fail.
It looks like the code that you have is doing this for efficiency. Whoever coded this, knows the structure of the string in myStr to sometimes have a non-numeric symbol in the third position. That's why he made this optimization to check the third symbol before paying for the conversion of the character array to string which then gets parsed.
Chances are, this optimization is premature: although making a temporary throw-away string is not free, this optimization would make sense only in situations when you do it a lot in a very tight loop. In other words, you do it only if it shows up near the top in your performance profiler's output.
You can optimize this check to avoid if:
int num1 = 0;
valid &= !Char.IsDigit(myStr[2]) || Int32.TryParse(myStr[2].ToString(), out num1);
I don't believe you need the first part (it could also throw an IndexOutOfRangeException).
So I would probably use:
int num1 = 0;
if (myStr.Length > 2 && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{
valid = false;
}
Char.IsDigit Method (String, Int32)
Indicates whether the character at the specified position in a specified string is categorized as a decimal digit.
Link
Int32.TryParse Method
Converts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the operation succeeded. This member is overloaded.
Link
Edit:
First I wrote that you can skip any of the check but now I am writing that you can not because
if (Char.IsDigit(myStr[2]) && Int32.TryParse(myStr[2].ToString(), out num1) == false)
{ }
Char.IsDigit() will return true if myStr[2] contains any of the Unicode characters listed here but Int.TryParse() will not convert any numbers except for 0-9 (not sure about this, as I have not checked all of them) so it will return false which is you are checking...
The condition you are checking can be understood by the following example:
string x = "AS௭s";
int s = 0;
if (Char.IsDigit(x[2]) && int.TryParse(x[2].ToString(), out s) == false)
{
// even if '௭` is Tamil Digit Seven and 'Char.IsDigit()' will return true but
// int.TryParse() will return false because it can not convert it
// so you are setting valid = false when the myStr contains a valid Unicode Character
// for a digit but It can not be converted to integer by TryParse method...
valid = false;
}
#Marc Gravell♦'s answer is the best solution for checking this condition...
Here's how I'd write it:
int num1 = 0;
try
{
num1 = Int32.Parse(myStr[2].ToString());
}
catch (Exception)
{
valid = false;
}
This does the same thing and is a lot easier to read imho, oh & you can log failed parses inside the catch.
Or you can do:
int num1 = 0;
valid = Int32.TryParse(myStr[2].ToString(), out num1);
I'm having a problem getting TryParse to work correctly for me. I have a list of values that I am almost assured are valid (as they come from another component in our system) but I would like to make sure there is proper error handling in place.
Here is an example list of my values:
20.00
20.00
-150.00
And here is the method I originally wrote:
private decimal CalculateValue(IEnumerable<XElement> summaryValues)
{
decimal totalValue = 0;
foreach (XElement xElement in summaryValues)
{
decimal successful;
Decimal.TryParse(xElement.Value, out successful);
if (successful > 0)
totalValue += Decimal.Parse(xElement.Value);
}
return totalValue;
}
The variable 'successful' was returning false for -150.00, so I added NumberStyles:
private decimal CalculateValue(IEnumerable<XElement> summaryValues)
{
decimal totalValue = 0;
foreach (XElement xElement in summaryValues)
{
decimal successful;
Decimal.TryParse(xElement.Value, NumberStyles.AllowLeadingSign, null, out successful);
if (successful > 0)
totalValue += Decimal.Parse(xElement.Value, NumberStyles.AllowLeadingSign);
}
return totalValue;
}
However, now that I have the NumberStyles in there, none of the numbers will parse! I feel good about having IFormatProvider set to null as this is all within our system. Does anyone see what I may be doing wrong?
This is not how you are supposed to use TryParse.
TryParse returns a boolean (true/false), so your code above should be:
private decimal CalculateValue(IEnumerable<XElement> summaryValues)
{
decimal totalValue = 0;
foreach (XElement xElement in summaryValues)
{
decimal valueReturned;
bool successful = Decimal.TryParse(xElement.Value, out valueReturned);
if (successful)
totalValue += valueReturned;
}
return totalValue;
}
or more succinctly,
private decimal CalculateValue(IEnumerable<XElement> summaryValues)
{
decimal totalValue = 0;
foreach (XElement xElement in summaryValues)
{
decimal valueReturned;
if (Decimal.TryParse(xElement.Value, out valueReturned))
totalValue += valueReturned;
}
return totalValue;
}
Others are explaining how to do it right, but not really explaining what you're doing wrong.
Where you're using "successful" above isn't the success value, it's the actual number that is being parsed. So if you're parsing "-150.00" of course successful will be negative. The out value of TryParse is the actual parsed value and the boolean indicating whether the process was successful or not is the returned value. Using what you have to help understand would be something like:
string inputValue = "-150.00";
decimal numericValue;
bool isSucessful = Decimal.TryParse(inputValue , out numericValue);
In this case, isSuccessful will be TRUE, numericValue will be -150. When you're using user-provided values instead of the hardcoded one I used above you'll want to check:
if(isSuccessful)
{
// Do something with numericValue since we know it to be a valid decimal
}
else
{
// Inform User, throw exception, etc... as appropriate, Don't use numericValue because we know it's wrong.
}
The other answers have got the right idea with regard to the proper way to use Decimal.TryParse. However, if I were writing the method in question, I'd use LINQ to work with LINQ-to-XML objects:
private decimal CalculateValue(IEnumerable<XElement> summaryValues)
{
return summaryValues
.Sum(el =>
{
decimal value;
if (Decimal.TryParse(el.Value, out value))
return value;
return 0M;
});
}
This version works the exact same way, but it uses the Enumerable.Sum method to calculate the total. All I have to supply is an inline function that extracts decimal values from an XElement.
Came in from Google. Answer for me was the incoming culture was wrong - specifically in an incoming JSON file.
Use
totalValue += decimal.Parse(xElement.Value, NumberStyles.Any, CultureInfo.InvariantCulture);
or
bool successful = decimal.TryParse(xElement.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
your successful is going to be negative for a negative value being parsed. your if (successful > 0) is what's tripping you up.
If they are almost positively going to be valid values, try using Convert.ToDecimal:
decimal val = Convert.ToDecimal(xElement.Value);
Otherwise, change your logic a bit to be more like:
decimal val;
if (Decimal.TryParse(xElement.Value, out val)){
// valid number
}
I would suggest you to tell XElement which node value it should look for as in:
XElement.Element("nodename").Value
Instead of XElement.Value. at least that is what I would do :)