Let no good deed go unpunished--why you can't be too careful with C#DecimalParse NumberStyles

R

raylopez99

Here is a classic example of why C# is like beating your head against
the wall. I can tell you where the error is in the below code--it's
the "NumberStyles" property of DecimalParse (which extracts a decimal
from a string)--and how to fix it--you leave it out--but why? WHY?
Y?

We'll never know... that's just the way it is. Some things were meant
to be...

(PS--my debugger is horrible--it doesn't usually work--but if I were
to 'guess' the problem it would be the NumberStyles flags comprise
more than one flag and they are "OR'd" together. As you logic freaks
know, when you "OR" more than two logical flags, you get potential
problems. BUT, I have to point out I got this from MSDN, which had
this example (four flags) AND, no pun intended, you would think they
would have the flags done right so that you can use more than two
flags at the same time. UPDATE: I just tried it with one flag, two
flags and it still fails for the NumberStyles version)

Below is the complete code, cut and paste and it should work to demo
the problem.

See the comments for where the 'error' is. I also am using a
NumberStyles I got from MSDN Help--so it should have worked--but did
not.

Here is the MSDN link for TryParse (similar to Decimal Parse):
http://msdn.microsoft.com/en-us/library/zf50za27.aspx
And here for NumberStyles: http://msdn.microsoft.com/en-us/library/system.globalization.numberstyles.aspx

RL

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;

namespace Console1
{
class Program
{
static void Main(string[] args)
{
NumberStyles styles = NumberStyles.AllowParentheses |
NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite |
NumberStyles.AllowTrailingWhite;

//PLAIN GENERIC NUMBERSTYLES! WHY DOES C# HAVE TO BE SO DIFFICULT,
WHY, why, Y?

string s1 = "1.23";

decimal d1 = DecimalParse(styles, s1);

Console.WriteLine("s1, d1 are: {0}, {1}", s1, d1); //try
fails, catch thrown. you get d1=0.99 in decimal returned, indicating
the catch block was executed

//now leave out NumberStyles--and see what happens (it works)

decimal d2 = DecimalParse2(s1);

Console.WriteLine("s1, d2: {0}, {1}", s1, d2); //you get
d2=1.23 in decimal returned, correct.
// output:
/*
* parse failed: Input string was not in a correct
format.s1, d1 are: 1.23, 0.99
* s1, d2: 1.23, 1.23
* Press any key to continue . . .
*
* */

// just for shi it s and grins, see if a non-static version makes a
difference (it does not, as I expected)

Program myProgram = new Program();

decimal aDecimal;

aDecimal = myProgram.nonStatic_DecimalParse(styles, s1);

Console.WriteLine("s1, aDecimal for non-static
DecimalParse: {0}, {1}", s1, aDecimal);
// still fails, like first static example above

//now should succeed, if you leave out NumberStyles

aDecimal = myProgram.nonStatic_DecimalParse2(s1);
Console.WriteLine("s1, aDecimal for non-static
DecimalParse2: {0}, {1}", s1, aDecimal);
//yes, succeeds to give 1.23, 1.23 as wanted
}

static decimal DecimalParse(NumberStyles styles, string
decimalString)
{
try
{
decimal decimalNumber;

decimalNumber = Decimal.Parse(decimalString, styles);
Debug.Write("decimalNumber is: " +
decimalNumber.ToString());
return decimalNumber;

}
catch (Exception ex)
{
// Display the exception message if Parse failed
Console.Write("parse failed: " + ex.Message);
return 0.99M;
}
} //

// leaving out NumberStyles version of method here:

static decimal DecimalParse2(string decimalString)
{
try
{
decimal decimalNumber;

decimalNumber = Decimal.Parse(decimalString);
return decimalNumber;

}
catch (Exception ex)
{
// Display the exception message if Parse failed
Console.Write("parse failed: " + ex.Message);
return 0.69M;
}
} //

decimal nonStatic_DecimalParse(NumberStyles styles, string
decimalString)
{
try
{
decimal decimalNumber;

decimalNumber = Decimal.Parse(decimalString, styles);
Debug.Write("decimalNumber is: " +
decimalNumber.ToString());
return decimalNumber;

}
catch (Exception ex)
{
// Display the exception message if Parse failed
Console.Write("parse failed: " + ex.Message);
return 0.99M;
}
} //

decimal nonStatic_DecimalParse2(string decimalString)
{
try
{
decimal decimalNumber;

decimalNumber = Decimal.Parse(decimalString);
return decimalNumber;

}
catch (Exception ex)
{
// Display the exception message if Parse failed
Console.Write("parse failed: " + ex.Message);
return 0.69M;
}
} //


}
}
 
J

Jon Skeet [C# MVP]

aylopez99 said:
Here is a classic example of why C# is like beating your head against
the wall.

No, it's an example of the .NET framework not working the way you want
it to. As I've said before, it's important to distinguish between the
language and the framework. C# as a language doesn't know anything
about NumberStyles.

<snip>

Here's a short but complete program showing the actual problem. I had
to do various bits of fixing with your original code due to line
wrapping. It's generally worth posting programs with no lines longer
than 78 characters.

using System;
using System.Globalization;

class Program
{
static void Main(string[] args)
{
NumberStyles styles = NumberStyles.AllowParentheses |
NumberStyles.AllowLeadingSign |
NumberStyles.AllowLeadingWhite |
NumberStyles.AllowTrailingWhite;

decimal.Parse("1.23", styles);
}
}

It's *much* clearer to just present a program as short as the above and
say, "This is throwing a System.FormatException with the message 'Input
string was not in a correct format' and I don't know why," than to
include the kind of massive program you did.

Now for the actual problem... fix is simple: include
NumberStyles.AllowDecimalPoint.
 
G

Göran Andersson

raylopez99 said:
Here is a classic example of why C# is like beating your head against
the wall.

You seem to be good at that head beating thing... ;) and once again
there is nothing wrong with the programming language...
I can tell you where the error is in the below code--it's
the "NumberStyles" property of DecimalParse (which extracts a decimal
from a string)--and how to fix it--you leave it out--but why? WHY?
Y?

Because it's incorrect.
We'll never know... that's just the way it is. Some things were meant
to be...

Well, Jon spotted the problem right away, so now we know...
(PS--my debugger is horrible--it doesn't usually work--but if I were
to 'guess' the problem it would be the NumberStyles flags comprise
more than one flag and they are "OR'd" together. As you logic freaks
know, when you "OR" more than two logical flags, you get potential
problems.

Why do you think that? What potential problems could possibly come from
or:ing more than two values?
BUT, I have to point out I got this from MSDN, which had
this example (four flags) AND, no pun intended, you would think they
would have the flags done right so that you can use more than two
flags at the same time. UPDATE: I just tried it with one flag, two
flags and it still fails for the NumberStyles version)

You obviously took the flags from an example that was parsing an integer
value. If you take example code intended for one purpose and use it for
something different without considering that it may need adjusting,
there is no surprise that it doesn't work as you expect.
Below is the complete code, cut and paste and it should work to demo
the problem.

See the comments for where the 'error' is. I also am using a
NumberStyles I got from MSDN Help--so it should have worked--but did
not.

Here is the MSDN link for TryParse (similar to Decimal Parse):
http://msdn.microsoft.com/en-us/library/zf50za27.aspx
And here for NumberStyles: http://msdn.microsoft.com/en-us/library/system.globalization.numberstyles.aspx

RL

8< snip another bloated piece of example code...
 
F

Family Tree Mike

The Decimal.TryParse example would have been better for you to follow. It
includes the AllowDecimal flag that Jon points out you need. I would assume
this is included in the default mode, where you omitted the NumberStyles,
and hence it worked. After all, what's the point of the Decimal type
without a decimal...
 
P

Pavel Minaev

Family Tree Mike said:
After all, what's the point of the Decimal type without a decimal...

Having 28 digits can be useful at times even if you don't bother with the
fractional part.
 
R

raylopez99

The Decimal.TryParse example would have been better for you to follow.  It
includes the AllowDecimal flag that Jon points out you need.  I would assume
this is included in the default mode, where you omitted the NumberStyles,
and hence it worked.  After all, what's the point of the Decimal type
without a decimal...

I assumed that the default would include "AllowDecimal", in other
words, that the flags would be more inclusive rather than all-
inclusive.

I was wrong.

Thanks for your help, and sorry to bother you.

In fact, in the actual program, I got rid of these NumberStyles, and
instead depend on an ErrorProvider component to kick in during
Validating event of the forms to catch any errors as the user types
them--much clearer to me, and easier to code.

RL
 
R

raylopez99

Why do you think that? What potential problems could possibly come from
or:ing more than two values?


Well, what is the result of this:

X AND Y AND Z

where X,Y,Z are 0 or 1 (so you have nine combinations)

consider:

(X AND Y) AND Z

versus

(X) AND (Y AND Z)

versus

X AND Y AND Z

I think it makes a difference. Same for OR, but to a lesser degree
(if at all). Same for XOR. That's why I don' combine more than two
flags at a time.

RL
 
J

Jon Skeet [C# MVP]

Well, what is the result of this:

X AND Y AND Z

where X,Y,Z are 0 or 1 (so you have nine combinations)

consider:

(X AND Y) AND Z

versus

(X) AND (Y AND Z)

versus

X AND Y AND Z

I think it makes a difference.

Then please show an example. It really doesn't - ditto with OR.

There *is* a difference between
(X OR Y) AND Z
X OR (Y AND Z)
but that's a different matter.

For each bit position, X OR Y OR Z the bit will be set if *any of* X,
Y or Z have that bit set, regardless of bracketing.

For each bit position, X AND Y AND Z the bit will be set if *all of*
X, Y and Z have that bit set, regardless of bracketing.

If bit flags didn't allow more a combination of more than 2
possibilities, they'd be nearly useless.

Jon
 
R

raylopez99

Well, what is the result of this:

X AND Y AND Z

where X,Y,Z are 0 or 1 (so you have nine combinations)

Actually, I made a mistake: it's 27 combinations (3x3x3), some of
which are symmetrical and repeat, but it's a lot of combinations, more
than nine.

RL
 
J

Jon Skeet [C# MVP]

Actually, I made a mistake:  it's 27 combinations (3x3x3), some of
which are symmetrical and repeat, but it's a lot of combinations, more
than nine.

No, there are 8. If each of X, Y or Z can be 0 or 1, so there are 2 to
the power of 3 combinations. Here they all are, including the result
for X AND Y AND Z:

X Y Z (X AND Y AND Z)
0 0 0 0
0 0 1 0
0 1 0 0
0 1 1 0
1 0 0 0
1 0 1 0
1 1 0 0
1 1 1 1

Jon
 
J

J. Clarke

raylopez99 said:
Well, what is the result of this:

X AND Y AND Z

where X,Y,Z are 0 or 1 (so you have nine combinations)

consider:

(X AND Y) AND Z

versus

(X) AND (Y AND Z)

versus

X AND Y AND Z

I think it makes a difference. Same for OR, but to a lesser degree
(if at all). Same for XOR. That's why I don' combine more than two
flags at a time.

Simple rule for a string of ANDs -- if any value is <false> the result
is <false>.

Simple rule for a string of ORs -- if any value is <true> the result
is <true>.


Google "Boolean algebra laws". You'll find several good explanations.
Once you understand the laws you'll be able to answer questions such
as this easily.
 
F

Family Tree Mike

I hadn't had that need come up yet, but it makes sense. Thanks for the
advice!
 
R

Rudy Velthuis

No, it doesn't:

X Y Z (x & y) & z x & (y & z)

0 0 0 0 & 0 = 0 0 & 0 = 0
1 0 0 0 & 0 = 0 1 & 0 = 0
0 1 0 0 & 0 = 0 0 & 0 = 0
1 1 0 1 & 0 = 0 1 & 0 = 0
0 0 1 0 & 1 = 0 0 & 1 = 0
1 0 1 0 & 1 = 0 1 & 0 = 0
0 1 1 0 & 1 = 0 0 & 1 = 0
1 1 1 1 & 1 = 1 1 & 1 = 1

No matter how many ANDs there are, the result is only 1 if ALL inputs
are 1 (i.e. if X = 1 and Y = 1 and Z = 1). Similarly for OR: the result
is only 0 if all inputs are 0 (i.e. if X = 0 and Y = 0 and Z = 0).

--
Rudy Velthuis http://rvelthuis.de

"#3 pencils and quadrille pads."
-- Seymoure Cray (1925-1996) when asked what CAD tools he used
to design the Cray I supercomputer; he also recommended using
the back side of the pages so that the lines were not so
dominant.
 
R

raylopez99

Simple rule for a string of ANDs -- if any value is <false> the result
is <false>.

Simple rule for a string of ORs  -- if any value is <true>  the result
is <true>.

Google "Boolean algebra laws".  You'll find several good explanations.
Once you understand the laws you'll be able to answer questions such
as this easily.

Karnough map and DeMorgan's theorem comes to mind. Yeah I think
you're right. Anyway, flags have to be set up so that they obey
Boolean Algebra--otherwise these neat logical symmetries don't work.
That's why flag enumerations are or should be in powers of two.

RL
 
R

raylopez99

  0  0  0     0 & 0 = 0     0 & 0 = 0
  1  0  0     0 & 0 = 0     1 & 0 = 0
  0  1  0     0 & 0 = 0     0 & 0 = 0
  1  1  0     1 & 0 = 0     1 & 0 = 0
  0  0  1     0 & 1 = 0     0 & 1 = 0
  1  0  1     0 & 1 = 0     1 & 0 = 0
  0  1  1     0 & 1 = 0     0 & 1 = 0
  1  1  1     1 & 1 = 1     1 & 1 = 1

No matter how many ANDs there are, the result is only 1 if ALL inputs
are 1 (i.e. if X = 1 and Y = 1 and Z = 1). Similarly for OR: the result
is only 0 if all inputs are 0 (i.e. if X = 0 and Y = 0 and Z = 0).


Thanks. Of course you're right, but in a real world AND, OR, NOR,
NAND you would have setup and hold problems so that X&Y would not
equal Y&X if outside the specified setup/hold times, but I'm just
being a smart aleck. So if X&Y != Y&X, then you do have 27 triplets,
and I was "right all along".

RL
 
M

Marc Gravell

but in a real world AND, OR, NOR, NAND you would have setup and hold problems

As far as C# etc are concerned, the "real world" is that AND and OR
are commutative.
So if X&Y != Y&X, then you do have 27 triplets,

No; if (with regular C# flags arithmetic) X&Y != Y&X, then your
computer needs a new CPU
and I was "right all along".

Or you just a major issue accepting that you are not perfect (none of
us are).
Get over it; we all make mistakes - only a fool refuses to accept
them. The wise also learn from them.

Marc
 
R

raylopez99

No; if (with regular C# flags arithmetic) X&Y != Y&X, then your
computer needs a new CPU


Or you just a major issue accepting that you are not perfect (none of
us are).

That's correct. Logically either I was "right all along"*, since I
said that the logic would have to be faulty (outside of the design
parameters of the logic), OR, as you say, I have a major issue
accepting I am not perfect. So I was correct in my statement, as you
acknowledge.

Ball is back in your court, Mr. Marc Gravell.
Get over it; we all make mistakes - only a fool refuses to accept
them. The wise also learn from them.

Then I'm a fool. Takes all kinds, or would you rather have a bunch of
brainiacs? You'd never get ahead in life if it wasn't for fools,
though I guess the world would be better off (albeit everybody would
be equal, which would be a problem).

RL

* since I knew all this during the OP, but I temporarily forgot it.
 
P

Pavel Minaev

Family Tree Mike said:
I hadn't had that need come up yet, but it makes sense. Thanks for the
advice!

This sort of thing might come up if you ever find yourself developing a
payroll application for a Zimbabwean company :)
 
G

Göran Andersson

raylopez99 said:
Well, what is the result of this:

X AND Y AND Z

where X,Y,Z are 0 or 1 (so you have nine combinations)

consider:

(X AND Y) AND Z

versus

(X) AND (Y AND Z)

versus

X AND Y AND Z

I think it makes a difference. Same for OR, but to a lesser degree
(if at all).

No, there is no difference. I think that you have to brush up on your
boolean algebra...

(Associativity of ?): x?(y?z) = (x?y)?z
(Associativity of ?): x?(y?z) = (x?y)?z

http://en.wikipedia.org/wiki/Boolean_algebra_(introduction)
Same for XOR.

Of course, but that's a different operator.
That's why I don' combine more than two
flags at a time.

I see. Because you believe things that plainly are not true... That's
not a very good reason.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top