John Stagich's Blog

Microsoft .Net Developer

.NET Math.Round, MidPointRounding Overload

clock February 11, 2015 15:32 by author JohnStagich

I have been working with the Microsoft .NET framework for a while.  The other day I learned something new.  I was using the Math.Round method, and I noticed that it was not working the way that I expected.  Here is what I mean.  I had a value of .3650, and I was rounding it to two decimal places Math.Round(.3650,2).  I was expecting the result to round up to .37, but instead it rounded down to .36.  What's going on? 

After some research, I found that the Math.Round method in .NET, by default, follows the round-to-even rule or bankers rounding.  From MortgageLoanCalculating.com:

“In grade school most of us learned that when the remainder at the rounding position is .5 or above we round up and if less than .5 we round down. When dealing with small amounts of data, this rounding method does not present a problem. However, when working with large sets of numbers, this rounding method produces results that will be skewed upwards. To address this problem, a new rounding method, sans the asymmetrical frequency distribution, was developed.

“With the round-to-even rule, when the remainder at the rounding position is .5, that number is rounded up when the number before it is odd, and rounded down when the number before it is even. For example, the number 6.5, using the round-to-even rule, would round down to the even number 6.0, while the number 7.5 would round up to the even number 8.0 -- hence the name round-to-even rule. The round-to-even rule is also referred to as bankers rounding because, not surprisingly, this rounding method is often used by bankers.”

So in my case with Math.Round(.3650,2), since the number before .50 was even, 6, it rounded down to .36.  Not what I wanted.  The fix, the Math.Round method comes with an overload that takes a MidpointRounding value.

Using the overload Math.Round(.3650,2,MidPointRounding.AwayFromZero), my code returned .37, which was what I wanted.

Why does the Math.Round method default to the round-to-even rule or bankers rounding?  Apparently, it is following the IEEE 754 standard.

I also tried rounding .3650 to two decimal places using the T-SQL and Excel rounding functions.  In both cases, the result was .37. 

Below are links I used to gather my information.

http://msdn.microsoft.com/en-us/library/System.Math.Round(v=vs.110).aspx#Round3_Example

http://stackoverflow.com/questions/977796/why-does-math-round2-5-return-2-instead-of-3-in-c



Using the .NET ExpandoObject Class

clock February 4, 2015 14:37 by author JohnStagich

Background

On my current assignment at an aluminum company, I was working on a .NET/SQL Windows Forms application.  I was asked to dynamically add columns to a DataGridView like control (ComponentOne’s C1FlexGrid control). 

 

Before assigned this task, I was populating the grid with a generic list of objects: List<PricePageGrid>.

 

List<PricePageGrid>  pricePageGridList  = new List<PricePageGrid>();

 

public class PricePageGrid

{

    public string Alloy { get; set; }

    public string Shape { get; set; }

    public string Size { get; set; }

    public decimal BasePrice { get; set; }

    public string Weight { get; set; }

    public decimal PriceAdder { get; set; }

    public decimal PercentageAdjustment { get; set; }

    public decimal QuantityDiscountPrice { get; set; }

}

// Set the DataSource property of the grid to the pricePageGridList

PricePageGrid.DataSource = pricePageGridList;

 

I was then told that my PricePageGrid class now needed to accommodate a variable number of properties, which needed to be displayed in the grid.  More specifically, the class needed to accommodate a variable number of Temper Adders from a TemperAdderList, and for each item in the list, add a new property along with its value to the PricePageGrid object.  Below is some pseudo code that may help clarify what needed to be done.

 

public List<TemperAdder> TemperAdderList { get; set;}

 

public class PricePageGrid

{

    public string Alloy { get; set; }

    public string Shape { get; set; }

    public string Size { get; set; }

    public decimal BasePrice { get; set; }

    public string Weight { get; set; }

    public decimal PriceAdder { get; set; }

    public decimal PercentageAdjustment { get; set; }

    public decimal QuantityDiscountPrice { get; set; }

 

   // For each instance of the class, add a Temper Adder property along with its value for each TemperAdder item in the TemperAdderList.

    public object_type TemperAdder_1  {get; set; }  // Temper Adders 1 to N

    ...

    public object_type TemperAdder_N {get; set; }

}

Solution

 

After doing some research, I found the ExpandoObject class (System.Dynamic.ExpandoObject) in the .NET 4.0 Framework.  It represents an object that allows you to dynamically add and remove members at run time.  It was what I needed.  However, I quickly had a problem.  The example ExpandoObject code that I was copying into my application would not compile.  The fix: I needed to add a reference to the Microsoft.CSharp.dll to my project.

 

To make the ExpandoObject code readable, I found some code on the web from Jonathon Sullinger that neatly wraps some of the functionality of the ExpandoObject class.  Here is the code that I ended up with. 

 

public class DynamicPricePage

{

    public dynamic Instance = new ExpandoObject();

 

    public void AddProperty(string name, object value)

    {

        ((IDictionary<string, object>)this.Instance).Add(name, value);

    }

 

    public dynamic GetProperty(string name)

    {

        if (((IDictionary<string, object>)this.Instance).ContainsKey(name))

            return ((IDictionary<string, object>)this.Instance)[name];

        else

            return null;

    }

 

    public void AddMethod(Action methodBody, string methodName)

    {

        this.AddProperty(methodName, methodBody);

    }

}

 

Using the AddProperty() method in the DynamicPricePage class, I could easily add properties to the DynamicPricePage instance at runtime.  The next section will show you how.

Creating the Dynamic Object and Populating the List

 

1)       The code to create the dynamic object and set the “static” properties. 

Note: When assigning a value to a property, the property type becomes the type of the value.  So for instance, the type of the Weight property will be a string, because it was assigned a string value “0.0”.

 

        DynamicPricePage ppg                                           = new DynamicPricePage();

       

        ppg.Instance.Alloy                                                  = AlloyDescription;

        ppg.Instance.Shape                                                = ShapeDescription;

        ppg.Instance.Size                                                    = DimensionDescription

        ppg.Instance.BasePrice                                          = BasePricePerUOM;

        ppg.Instance.Weight                                              = “0.0”;

        ppg.Instance.PriceAdder                                       = 0.0m;

        ppg.Instance.PercentageAdjustment                  = 0.0m;

        ppg.Instance.QuantityDiscountPrice                   = ppg.Instance.BasePrice + PriceAdder;

 

2)       The code to dynamically add the variable number of properties along with their values from the TempeAdderList to the DynamicPricePage object.

 

        foreach (TemperAdder ta in TemperAdderList)

        {

                        ppg.AddProperty(ta.Description, ta.PriceAdder)  // property name and value

        }

 

3)       Add the dynamic object to a list of DyamicPricePage objects.

List<DynamicPricePage> pricePageGridList = new List<DynamicPricePage>();


pricePageGridList.Add(ppg);


Converting the List of Dynamic Objects to a Table

Now that I had the data that I wanted in a pricePageGridList, I assigned the pricePageGridList to the data source property of the grid: PricePageGrid.DataSource = pricePageGridList.  When I ran the application, no data appeared in the grid.  Why?  In short, the grid did not like the DynamicPricePage type of the pricePageGridList.

 

What next? The pricePageGridList was not of much use if I could not display its contents in the grid.  Could I convert the pricePageGridList to a table?  The answer, YES!


It was a three step process to convert the pricePageGridList to a table.  Here is the link where I found the code to convert the list of dynamic objects to a table.

 

1)       Cast a dynamic object to dictionary so we get the properties from it.

var dynamicPricePageProperties = pricePageGridList[0].Instance as IDictionary<string, object>;

 

2)       Create a DataTable and add columns to the table using the dynamicPricePageProperties acquired in step one.  Also, set the data type when adding a column.  In a table, if a column's data type is a string, you cannot apply a Format.  For example, pricePageGrid.Cols["BasePrice"].Format = "####0.00" will not work.

 

var table = new System.Data.DataTable();

foreach (var column in dynamicPricePageProperties)

{

if (Utility.IsDecimal(column.Value.ToString()))   // Utility.IsDecimal tries to parse the string: decimal.Parse(input);

table.Columns.Add(column.Key, typeof(decimal));

               else

                                table.Columns.Add(column.Key, typeof(string));

 }

3)       Populate the table created in step two using the pricePageGridList

 

              foreach (DynamicPricePage dynamicPricePage in pricePageGridList)

              {

                        DataRow row = table.NewRow();

                        System.Collections.Generic.IDictionary<string, object> dictionaryCollection =   dynamicPricePage.Instance as IDictionary<string, object>;

                        foreach (var key in dictionaryCollection.Keys)

                        {

                            row[key] = dictionaryCollection[key];   // Uses the key to retrieve the Value of the item in the dictionaryCollection                       

                        }

                        table.Rows.Add(row);

              }

 

Once I converted the list of dynamic objects into a table, I assigned the table to the DataSource property of the grid (PricePageGrid.DataSource = table;), and the data now appeared in the grid when I ran the application.



About the author

I am the owner of Stagich Software Consulting.  Stagich Software Consulting specializes in developing Microsoft .Net/Microsoft SQL software applications.

 

Calendar

<<  November 2024  >>
MoTuWeThFrSaSu
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

Sign In