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

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.

You should do this in sql query using
ROUND(table1.IntCol 1 - table2.IntCol 2, 1)

Related

Floor behavior with number 1.9999999999999999d [duplicate]

This question already has answers here:
Why does floating-point arithmetic not give exact results when adding decimal fractions?
(31 answers)
Closed 5 years ago.
Look at this situation:
var number1 = Math.Floor(1.9999999999999998d); // the result is 1
var number2 = Math.Floor(1.9999999999999999d); // the result is 2
In a both cases, the result should be 1. I know it's a very unlikely scenario, but possible to occur. The same ocurr with Math.Truncate method and (int) cast.
Why does it happen?
There is no exact double representation for a lot of numbers.
The double number the nearest from 1.9999999999999999 is 2, so the compiler rounds it up.
Try to print it before using your Math.Floor function !
However, the nearest from 1.9999999999999998 is still 1.something, so Floor gives out 1 .
Again, it would be enough to print the number before the function Floorto see that they were actually not anymore the one entered in the code.
EDIT : To print out the number with most precision :
double a1 = 1.9999999999999998;
Console.WriteLine(a1.ToString("G17"));
// output : 1.9999999999999998
double a2 = 1.9999999999999999;
Console.WriteLine(a2.ToString("G17"));
// output : 2
Since double precision is not always precise to 17 significative digits (including the first one before the decimal point), default ToString() will round it up to 16 significant digits, thus, in this case, rounding it up to 2 as well, but only at runtime, not at compile time.
If you put literals values into another variables, then you see it why:
var a1 = 1.9999999999999998d; // a1 = 1.9999999999999998d
var number1 = Math.Floor(a1);
Console.WriteLine(number1); // 1
var a2 = 1.9999999999999999d; // a2 = 2
var number2 = Math.Floor(a2);
Console.WriteLine(number2); // 2
As for why - this has to be something to do with precision of double and decision of compiler as to what value to use for a given literal.

Divide between two rows of the same column and store the answer in a already existing column

I have a table in my database that has columns 'price', 'percentage' and 'ID'.
Percentage column needs to be calculated using colum 'price' and 'id'. There is an initial price, followed by three increased prices, all of them having the same ID. I need to find a method that calculates the percentage column in my database using C#.
I attach a picture for better understanding how it should be.
I am writing an import system using Linq and I have already imported all the columns, but now I need to calculate percentage for the increasing prices and I am really struggeling with this. Maybe someone have some god suggestions of how I can solve this.
UPDATE:
public static void calculateProcentage(string id, double price, double, double percentage)
{
var percentageQuery = from l in db.Table
where l.ID == id
&& l.Price == price && l.Percentage != percentage
select l;
foreach (Table l in percentageQuery)
{
//double newPercentage = (double) l.Percentage;
//DataTable table = new DataTable();
// int rowCount = table.Rows.Count;
DataGridView dataGridView = new DataGridView();
for (int i = 0; i < dataGridView.Rows.Count; i++)
{
if (l.Building_vigor_ID == building_vigor_id)
{
//var priceRows = db.V_Leases.Select(x => x.Price_sqm).ToList();
// get a list of the rows of the price column
// get a list of the rows of the price column
var priceRows = (from r in db.Table
select r.Price).ToList();
percentage = (double)(priceRows[i] / priceRows[i + 1]);
}
}
}
try
{
db.SubmitChanges();
Console.Write("Percentage updated");
//Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine(e);
Console.Write("Could not update percentage");
//Console.ReadKey();
}
}
That is what I have tried. I bassicaly wanted to make it like an update method with only updating column percentage. But did not actualy work. I am pretty new with Linq sow it may be some bad code written here.
The disclaimer
I am pretty new with Linq sow it may be some bad code written here.
Your question, and the way you've attempted to solve this, are fraught with inconsistencies and seemingly contradictory expectations.
As it stands, the question you've asked is not answerable due to lack of information. However, I do think I understand what the actual problem is that you're trying to solve, so I'll give you that answer. But first, let me explain how I've interpreted your question.
My assumptions about your actual question
As I understand it, you're trying to calculate the percentage based on the previous value.
A more generalized example:
PRICE % ID
------------------------
100 A 1
103 B 1
100 C 2
150 D 2
A and C should both be 0 as they are the "base" price of their respective ID value.
B should be 3% because its price is 103% of A's price.
D should be 50% because its price is 150% of C's price.
My below answer will assume that this is correct.
There is also a problem with your expected values. In your example, you have listed the percentage of 19.79 (compared to 19.21) as 0.3.
This does not make sense. The difference is 3%. There are two different (acceptable) ways to denote this in the percentage column:
3, because it is expressed as a percentage (3%)
0.03, because it is expressed as a decimal value (3% = 0.03)
Your usage of 0.3 makes no sense, as I would interpret this as either 0.3% (option 1) or 30% (option 2).
In order to maintain consistency, my answer will assume that you want the decimal value (0.03 in the above example)
I assume your data class is constructed as follows:
public class Foo
{
public decimal Price { get; set; }
public decimal Percentage { get; set; }
public int ID { get; set; }
}
I don't quite understand how you're using the method. You supply three parameters (string id, double price, double, double percentage), but then you go on to select your data as follows:
var percentageQuery = from l in db.Table
where l.ID == id
&& l.Price == price && l.Percentage != percentage
select l;
It makes little sense to supply a percentage, and then pick everything that's different from that percentage. You have no idea of knowing what data you're going to get, in what order, and whether or not the found entries are "before" or "after" your mentioned percentage.
Instead, my answer will be a method that recalculates all percentages of a given ID. This seems like a much clearer algorithm.
The assumed answer
Retrieving the data
Your attempt is a weird mix of new and old technologies (LINQ, DataTable), I'm going to use LINQ near exclusively, as I feel the use of DataTable is unwarranted here.
public static void CalculatePercentagesForID(int id)
{
Foo[] rows = db.Table.Where(x => x.ID == id).ToArray();
//... (see the next code block)
}
This is much simpler. Note that I am assuming that you wish to process the entries based on the order that they appear in the database. If you need to order them based on something else (e.g. a date value in your Foo objects), then you will have to use an additional OrderBy, e.g. :
Foo[] rows = db.Table.Where(x => x.ID == id).Orderby(x => x.DateCreated).ToArray();
Processing the data
It's important to notice here that a percentage is calculated off of two (subsequent) rows.
//The first entry is always the "base" price, therefore always 0.
rows[0].Percentage = 0;
for(int i = 1; i < rows.Length; i++)
{
var previous = rows[i-1];
var current = rows[i];
current.Percentage = CalculateDifferenceInPercentage(previous.Price, current.Price);
}
//TODO: save all the objects in "rows" back to the database!
Notice how the for loop starts at 1, not at 0. We skip step 0 because the first element is automatically 0 anyway. The for loop will only process every row after the first.
Calculating the percentage
public static Decimal CalculateDifferenceInPercentage(decimal oldPrice, decimal newPrice)
{
//1.The elaborate calculation
//The comments are example output when oldPrice = 19.21, newPrice = 19.79
var theRatio = newPrice / oldPrice;
// = 1.0301...
var theRatioRounded = Math.Round(theRatio,2);
// = 1.03
var thePercentageDifference = theRatioRounded - 1;
// = 0.03
return thePercentageDifference;
}
Or, if you want a shorter (but harder to read) version:
public static Decimal CalculateDifferenceInPercentage(decimal oldPrice, decimal newPrice)
{
return Math.Round(newPrice / oldPrice , 2) - 1;
}
Some caveats
I've omitted some null-checks for the sake of simplicity, e.g. checking if any rows are returned.
I've omitted how to save the updated entities in the database as it is not pertinent to your actual question about calculating the percentages.
This works both for negative and positive percentages.

How to convert from decimal to double in Linq to Entity

Suppose we have table T which has two columns A and B with float and money types respectively. I want to write a linq query like following T-SQL statement:
Select A, B, A * B as C
From SomeTable
Where C < 1000
I tried to cast like following
var list = (from row in model.Table
where ((decimal)row.A) * row.B < 1000
select new { A = row.A,
B = row.B ,
C = ((decimal)row.A) * row.B}
).ToList();
but it does not allow the cast operation. It throw an exception:
Casting to Decimal is not supported in Linq to Entity queries, because
the required precision and scale information cannot be inferred.
My question is how to convert double to decimal in Linq? I don't want to fetch data from database.
Update:
I notice the converting decimal to double works but reverse operation throws the exception. So,
Why can't we convert double to decimal? Does Sql server do the same mechanism in t-sql too? Doesn't it affect precision?
The difference between a float (double) and a decimal, is that a float is decimal precise. If you give the float a value of 10.123, then internally it could have a value 10.1229999999999, which is very near to 10.123, but not exactly.
A decimal with a precision of x decimals will always be accurate until the x-th decimal.
The designer of your database thought that type A didn't need decimal accuracy (or he was just careless). It is not meaningful to give the result of a calculation more precision than the input parameters.
If you really need to convert your result into a decimal, calculate your formula as float / double, and cast to decimal after AsEnumerable:
(I'm not very familiar with your syntax, so I'll use the extension method syntax)
var list = model.Table.Where(row => row.A * row.B < 1000)
.Select(row => new
{
A = row.A,
B = row.B,
})
.AsEnumerable()
.Select(row => new
{
A = row.A,
B = row.B,
C = (decimal)row.A * (decimal)row.B,
});
Meaning:
From my Table, take only rows that have values such that row.A * row.B
< 1000.
From each selected row, select the values from columns A and B.
Transfer those two values to local memory (= AsEnumerable),
for every transferred row create a new object with three properties:
A and B have the transferred values.
C gets the the product of the decimal values of transferred A and B
You can avoid AsEnumerable() explaining to Entity how many fractional digits you want.
var list = (from row in model.Table
where ((decimal)row.A) * row.B < 1000
select new { A = row.A,
B = row.B ,
C = (((decimal)((int)row.A)*100))/100) * row.B}
).ToList();

rounding up columns of values

I start with figures in line 1 for columns A nd B, (see below) 890 and 450 and both are divided by 1.2 to get a net figure (line 2), the results of which are then split over two types (say salesA and salesB)
How can I ensure that the sum of salesA + salesB will always be equal to the values shown on line 2 which in turn will be equal to values on line 1 when multiplied by 20%.
As you can see, column B is out by 0.02
Example
column A column B
890.00 /1.2 450.00 /1.2
741.67 375.00
333.33 salesA 208.35 salesA
408.34 salesB 166.67 salesB
741.67 375.02 sum of salesA + sales B
Values on line 2 multiplied by 20% should be equal to values on line 1
How can this be achieved ? I am aware of Math.Round.
Have tried inserting it everywhere but it doesn’t work
The division operator is inside a select statement
You are doing something like this when splitting:
total = 741.67
a = round(f(total), 2)
b = round(g(total), 2)
where f() and g() are the calculations used to split. Instead do this:
total = 741.67
a = round(f(total), 2)
b = total - a;
Now a + b are always equal to total.
However, you will never be able to get the starting value again when multiplying with 1.2 because of the rounding.

Do a Range Search for a Alphabetic Field

I have a application that I save this in the database:
FromLetter ToLetter
AAA AAZ
ABC MNL
what I need is to search like this AAC and returns record 1 and FBC and return record 2.
Is the same functionality if instead of letter I save dates. I need to do the same query.
I am using SQL Server and Entity Framework, any Idea how to do this?
Should be pretty straight forward. Here is a Linq to Entities solution, ignoring case:
Entity Framework/Linq solution strings:
string yourValue = somevalue;
var result = (from r in db.ExampleTable
where String.Compare(yourValue, r.FromLetter, true) == 1
&& String.Compare(yourValue, r.ToLetter, true) == -1
select r).First();
Dates:
DateTime yourValue = somevalue;
var result = (from r in db.ExampleTable
where yourValue >= r.FromDate
&& yourValue <= r.ToDate
select r).First();
I think it would be much easier to represent the FromLetter and ToLetter attributes using an integer. Especially if the length of the string is always just 3 - you can simply encode the number as:
(((letter1 - 'A') * 26 + (letter2 - 'A')) * 26) + (letter3 - 'A')
This will give you a number between 0 and 26^3 that represents the tripple and can be easily converted back to the string (using modulo and division as when converting numbers between numeric bases). This number fits into Int32 comfortably (up to 6 letters).
Searching for a string within a specified range would then be a simple search for an integer within a numeric range (which is easy to do and efficient).
Genius solution given by.... bunglestink
I wasted plenty of time in researching implementation of "between" clause for string in EF. This is helpful.

Categories