Monday, October 22, 2018 ..:: Home ::.. Register  Login

Peter Henry on Facebook  Peter Henry on LinkedIn  Peter Henry on Twitter

All blog entries are the opinions of the author and do not necessarily reflect the opinions of their employer. All the code presented is for explanation and demonstration purposes only. Any damages incurred to your site and/or data are not the responsibility of the author. Every effort is taken to ensure the code properly compiles, however sometimes there are some hiccups and you might be required to do your own debugging.
   TechTidBits (Blog)  

When is a a double an int? When you least expect it for DataTables apparently!

May 12

Written by:
Wednesday, May 12, 2010 9:55 PM  RssIcon

UI to show how to make the AVG() workToday I was trying to find the average value of a DataTable.  Seems simple, except the values coming back were always integers.  What was going on?  Read on for a sneaky tidbit which could be mucking up YOUR data!

UI to show how to make the AVG() workToday I ran SMACK into a very interesting problem.  Let me set it up for you. 

I have a DataTable with a bunch of rows'n'columns and I want to add a TOTALS row.  Sound  familiar so far right?  Next I want to "compute" the average values of one of the columns.  Sounds cool, easy enough, especially when there's the DataTable.Compute() method for me!  

COOL!  UNTIL!!!!!!!!!!!  you realize the nasty side of the Compute() method is it returns the SAME datatype as the column.  YES!  That's right, the very same datatype as the column.  Maybe YOU expected that, but I certainly didn't.  Why is that?  Compute returns back an object and I just figured if I'm doing division, it'll return me back something with the decimal which I can then cast to my own int or double, or decimal or whatever I want.  But nope, the decision's made for me, before I can cast it.

Ok, back to my challenge at hand.  Our client wants all their averages to use the MidpointRounding.AwayFromZero (please read this for further info), and to do THAT math on our averages column, I want to compute the AVG on the column (I expect the value coming back to be a double since I'm dividing right?) then I can cast/round/do what-ever I want with it.  Nope, sorry!  It's already cast for me when it returns BACK from the compute method.

Huh?  WTF?  Want more info?  Sure. 

I wrote a small test app for this, and at runtime, I put a breakpoint and checked out two of the Computed columns.  See how the average[NumberOfPlayers] is an int and the average[NumberOfPlayersDouble] is a double Type?  There's no casting done there, those are the native/default returning datatypes being returned via the Compute calculations.

Runtime datatypes of the AVG columns

The code to test above looks like this. 

DataRow average = table.NewRow();
average["TeamName"] = "Avg # Players";
average["NumberOfPlayers"] = table.Compute( "AVG(NumberOfPlayers)", "" );
average["NumberOfPlayersDouble"] = table.Compute( "AVG(NumberOfPlayersDouble)", "" );
average["NumberOfPlayersRounded"] =
   table.Compute( "AVG(NumberOfPlayersDouble)", "" )
  ), 0, MidpointRounding.AwayFromZero );
table.Rows.Add( average );

Now for the next issue.  When you set the value of average[NumberOfPlayersDouble] column, you're getting back an object which LOOKS like a double.  What's the big deal... the code will automatically truncate your double value to an int, ya, on your behalf!  DOH!  So, IF you want it to STAY a double, you'll have to explicitly cast it.  Here's the udpated code.

average["NumberOfPlayersDouble"] = Convert.ToDouble( table.Compute( "AVG(NumberOfPlayersDouble)", "" ) );

Now, when you run the code, you'll get something you expected, decimal points for your average calculation.

UI showing things working

The last point I want to make is our client wanted the average values rounded using the MidpointRounding.AwayFromZero enumeration.  This bascially means, every ".5" value gets upped to the next integer.  The main thing to note here is you CANNOT just truncate the average value or cast it to an integer cause from the above example, you'd consistently be off by one.  No good.  So in this case, I added another column (completely optional, you could just reuse the NumberOfPlayers integer column if you wanted to, I'm not to help illustrate my point).  Above you'll see the code using the Math.Round() method for the new column.

Now that you know how to correctly calculate averages, it's time to grab a coffee and get coding!




MSDN: DataTable.Compute Method

PCHenry: Have you tried using Math.Round() before? Did you REALLY test it out?

Location: Blogs Parent Separator TechTidBits

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Add Comment   Cancel 
Copyright 1999-2012 by   Terms Of Use  Privacy Statement