简体   繁体   中英

How can I add an accurate difference column to a DataTable?

I need to compare numeric data from two datatables with the same schema. For example two tables with data such as

PKColumn | numCol | DecimalCol

will end up looking like this after the merge

PKColumn | numCol 1 | numCol 2 | numCol Diff | DecimalCol 1 | DecimalCol 2 | DecimalCol Diff

Initially, I just created the diff column as an expression col1-col2 but I can end up with unusual looking values

col1    col2    diff        c1      c2      diff
12.8    14.6    -1.80000019 33.2    29.8    3.40000153

But what I want is:

col1    col2    diff        c1      c2      diff
12.8    14.6    -1.8        33.2    29.8    3.4

So my current solution is to manually iterate through the rows and set the value using this method:

private static void SetDifference(DataRow dataRow, DataColumn numericColumn)
{
    dynamic value1 = dataRow[numericColumn.Ordinal - 2];
    dynamic value2 = dataRow[numericColumn.Ordinal - 1];

    if (IsDbNullOrNullOrEmpty(value1) || IsDbNullOrNullOrEmpty(value2)) return;

    //now find out the most decimals used and round to this value
    string valueAsString = value1.ToString(CultureInfo.InvariantCulture);

    int numOfDecimals = valueAsString.SkipWhile(c => c != '.').Skip(1).Count();

    valueAsString = value2.ToString(CultureInfo.InvariantCulture);

    numOfDecimals = System.Math.Max(numOfDecimals, valueAsString.SkipWhile(c => c != '.').Skip(1).Count());

    double result = Convert.ToDouble(value1 - value2);

    dataRow[numericColumn] = System.Math.Round(result, numOfDecimals);
}

But it feels clunky and not good for performance. Suggestions for improvements are welcome.

EDIT: changed column names from "int" to "num" to avoid confusion

EDIT: also, I don't always want to round to one decimal spot. I may have data like numA: 28 numB: 75.7999954 so I want a diff of: -47.7999954

If you have columns that store integer numbers then use an integer number type for them! Probably you used a single precision floating point type. The same holds for decimal types. Use a decimal column type for them and this rounding problem will vanish!

If you use a true decimal type (not float, single, real or double) you will not encounter rounding problems. I do not know which database you are using, but with SQL-Server the correct column type would be decimal .


UPDATE

Since you cannot change the column type, round it to the requested precision like this

result = Math.Round((c1-c2) * 10)/10; // One decimal
result = Math.Round((c1-c2) * 100)/100; // Two decimals
result = Math.Round((c1-c2) * 1000)/1000; // Three decimals
Math.Round(3.141592654 * 10000)/10000  ===>  3.1416

--

UPDATE

Try this, it should perform well in most cases

decimal result = (decimal)col1 - (decimal)col2;

Test

12.8f - 14.6f  ===>  -1.80000019

(decimal)12.8f - (decimal)14.6f  ===>  -1.8

Based on Olivier's comments I've updated my code to look like this:

if(numericColumn.DataType == typeof(int))
{
    dataRow[numericColumn] = System.Math.Abs(value1 - value2);
}
else
{
    dataRow[numericColumn] = Convert.ToDecimal(value1) - Convert.ToDecimal(value2);
}

Seems a lot cleaner and it gets the job done. Thanks for the help.

你应该在sql查询中使用

ROUND(table1.IntCol 1 - table2.IntCol 2, 1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM