Matching excels rounding in a C# application - c#

I am currently in the process of turning a rather lofty Excel sheet that is used for calculating scientific values into a C# application. However, I am hitting some problems in regards to the rounding.
All of my values are stored as doubles, and when you perform a small number of operations on them they match the excel sheet within acceptable accuracy (5 or 6 decimal places). When they are put through rather large operations with division, multiplication, square roots. They start to drift off by quite a large margin. I switched the entire code base to decimals at another point to test if it resolved this issue, it lessened the gap but the issue still remained.
I am aware this is due to the nature of decimal numbers in software development, but it's imperative I match excels rounding as much as possible. Research on this topic points me towards the standards that excel uses to round and it seems C# by default uses a slightly different one. Despite learning of this I am still unsure of how to proceed on replicating excels rounding. I'm wondering if anyone has any advice or previous experience on this topic?
Any help would be greatly appreciated.
EDIT : I would just like to clarify that I am not rounding my numbers whatsoever. The rounding on both the sheet and my code is implicitly being applied. I have tested the same formulas inside of a totally different software package (A form builder called K2). The resulting numbers match my c# application so it seems excels implicit rounding differs in some way.
One of the offending formulas:
(8.04 * Math.Pow(10, -5)) *
(Math.Pow(preTestTestingDetails.PitotCp, 2)) * (DeltaH) *
(tempDGMAverage + 273.0) /
(StackTemp + 273) *
((preTestTestingDetails.BarometricPressure / 0.133322 +
((preTestTestingDetails.StackStaticPressure / 9.80665) / 13.6)) /
(preTestTestingDetails.BarometricPressure / 0.133322)) *
(preTestTestingDetails.EstimatedMolWeight /
((preTestTestingDetails.EstimatedMolWeight * (1 - (EstimatedMoisture / 100))) +
(18 * (EstimatedMoisture / 100)))) *
Math.Pow((1 - (EstimatedMoisture / 100)), 2) *
(Math.Pow(preTestTestingDetails.NozzleMean, 4));

In C# the result of
int x = 5;
var result = x / 2; // result is 2 and of type int
... because an integer division is performed. So if integers are involved (not a double with no decimals, but a value of type int or long), make sure to convert to double before dividing.
int x = 5;
double result = x / 2; // result is 2.0 because conversion to double is made after division
This works:
int x = 5;
var result = (double)x / 2; // result is 2.5 and of type double
int x = 5;
var result = x / 2.0; // result is 2.5 and of type double
int x = 5;
var result = 0.5 * x; // result is 2.5 and of type double
The only place in your formula where this could happen is EstimatedMoisture / 100, in case EstimatedMoisture is of type int. If this is the case, fix it with EstimatedMoisture / 100.0.
Instead of 8.04 * Math.Pow(10, -5), you can write 8.04e-5. This avoids rounding effects of Math.Pow!
I don't know how Math.Pow(a, b) works, but the general formula is a^b=exp(b*ln(a)). So instead of writing Math.Pow(something, 2), write something * something. This is both, faster and more accurate.
Using constants for magic numbers adds clarity. Using temps for common sub-expressions makes the formula more readable.
const double mmHg_to_kPa = 0.133322;
const double g0 = 9.80665;
var p = preTestTestingDetails;
double moisture = EstimatedMoisture / 100.0;
double dryness = 1.0 - moisture;
double pressure_mmHg = p.BarometricPressure / mmHg_to_kPa;
double nozzleMean2 = p.NozzleMean * p.NozzleMean;
double nozzleMean4 = nozzleMean2 * nozzleMean2;
double result = 8.04E-05 *
p.PitotCp * p.PitotCp * DeltaH * (tempDGMAverage + 273.0) / (StackTemp + 273.0) *
((pressure_mmHg + p.StackStaticPressure / g0 / 13.6) / pressure_mmHg) *
(p.EstimatedMolWeight / (p.EstimatedMolWeight * dryness + 18.0 * moisture)) *
dryness * dryness * nozzleMean4;
Why not use 273.15 instead of 273.0 if precision is a concern?

Related

Which numeric type conversion is better for simple math operation?

I want to know which conversion is better (regarding performance/speed and precision/most less loss) for a simple math operation and what makes them different?
Example:
double double1 = integer1 / (5 * integer2);
var double2 = integer1 / (5.0 * integer2);
var double3 = integer1 / (5D * integer2);
var double4 = (double) integer1 / (5 * integer2);
var double5 = integer1 / (double) (5 * integer2);
var double6 = integer1 / ((double) 5 * integer2);
var double7 = integer1 / (5 * (double) integer2);
var double8 = Convert.ToDouble(integer1 / (5 * integer2));
var double9 = integer1 / Convert.ToDouble(5 * integer2);
Actually my question is about the conversion not the type itself.
EDIT
In response to your totally changed question:
The first line double double1 = integer1 / (5 * integer2); does an integer division, so don't do that.
Also the line var double8 = Convert.ToDouble(integer1 / (5 * integer2)); is doing integer division before converting the result to a double, so don't do that either.
Other than that, all the different approaches you list will end up calling the IL instruction Conv.R8 once for each line in your sample code.
The only real difference is that Convert.ToDouble() will make a method call to do so, so you should avoid that.
The results for every line other than double1 and double8 will be identical.
So you should probably go for the simplest: var double2 = integer1 / (5.0 * integer2);
In a more complicated situation, time your code to see if there's any differences.
The differences between your lines of codes are not about conversions, some of them are totally different things and the values are not the same.
1. float float1 = integer1 / (5 * integer2);
5 * interger2 gives an int, int divides int gives an int, and you assign the int value to a float variable, using implicit conversion because int has a small range than float. float float1 = 1 / (5 * 2), you will get a System.Single 0 as the result.
2. var float2 = integer1 / (5.0 * integer2);
5.0 is essentially 5.0d, so the type of float2 is System.Double, var float2 = 1 / (5.0 * 2) you will get a System.Double 0.1 as the result.
3. var float3 = integer1 / (5F * integer2);
Using the same values above you will get a System.Single 0.1, which is probably what you want.
4. var float4 = (float)integer1 / (5 * integer2);
You will get the same as item 3. The difference is item3 is int divides by float while item4 is float divides by int.
5. var float5 = integer1 / (float) (5 * integer2);
6. var float6 = integer1 / ((float) 5 * integer2);
7. var float7 = integer1 / (5 * (float) integer2);
The three are almost the same as item 3, it calculates an int divides by float, just different ways to build the divider.
8. var float8 = Convert.ToDecimal(integer1 / (5 * integer2));
9. var float9 = integer1 / Convert.ToDecimal(5 * integer2);
The two will give you System.Decimal values, which has a higher precision. Item8 has the same issue as item1, you will get 0 because the parameter of Convert.ToDecimal is a int 0.
Instead of float/decimal use double:
A more generic answer for the generic question "Decimal vs Double": Decimal for monetary calculations to preserve the precision, Double for scientific calculations that do not get affected by small differences. Since Double is a type which is native to the CPU (internal representation is stored in base 2), calculations made with Double perform better then Decimal (which is represented in base 10 internally).

Resource intensive mathematical calculation method in .NET

I have a numerical computation method in my .NET code that will be called more than 1000 times.
private double CalculatePressureLossThroughPipe(double length, double flow, double diameter)
{
double costA = 0, costB = 0;
double frictionFactor = 0;
double pressure = 0;
double velocity = flow / CalculatePipeArea(diameter);
// calculate Reynods No
double reNo = ((this.mDensity * velocity * diameter) / this.mViscosity);
// calculate frictionFactor
costA = Math.Pow((2.457 * Math.Log(1 / (Math.Pow((7 / reynoldsNo), 0.9) + (0.27 * 0.000015) / diameter))), 16);
costB = Math.Pow((37530 / reNo), 16);
frictionFactor = (2 * Math.Pow(((Math.Pow((8 / reNo), 12)) + (1 / Math.Pow((costA + costB), 1.5))), 0.083333));
// Calulate Pressure
pressure = (DesignConstants.PRESSSURE_CONSTANT * 2 * frictionFactor * length * Math.Pow(velocity, 2) * this.mDensity / diameter);
return pressure;
}
This function will be called in a loop, with different set of input parameters. The loop itself is quite intensive which calls the above mentioned function (with unique parameters) every time.The function although looks small is quite resource intensive.Is there an alternate way to process the method calls without using the standard members from System.Math ?
It looks like the expression (Math.Pow((7/reynoldsNo), 0.9) + (0.27*0.000015)can be precalculated since it's not dependent on any of your inputs. In any case when you say this method 'is quite resource intensive' presumably you mean it takes a long time - have you benchmarked it ? What would an acceptable time be ? These are the things you need to find out before trying to optimise anything.
You could try to improve the performance using multiple threads (using Tasks / Threads) and vectorization.
Using System.Numerics you may be able to leverage the power of SIMD, possibly increasing performance 4 times.
First of all you should analyze all the mathematical expressions and
reduce the number of those that can be precalculated:
(0.27*0.000015)
also try to use multiplication instead of Math.Pow if possible: velocity * velocity would be faster than Math.Pow(velocity, 2)
if possible you can try Pow approximation algorithms - they are faster but not so precise. Look for more information this article: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
Are you using Parallel class for your loop to utilize multicore/multiprocessor of your PC? https://msdn.microsoft.com/library/dd537608(v=vs.110).aspx

Why am I getting zero when I multiply doubles?

I made this line of code while debugging:
double hola = (1 / 2) * (double)x.height;
height is a double. Hola is just a temporary name.
When I debug, I see that x.height = 1, and hola = 0.
What did I do wrong? I'm pretty sure I made some really simple mistake..
Also when I remove the double casting that I do to x.height I still get hola = 0.
1 / 2 is zero, remainder one. Zero times anything is zero.
Did you mean to write 1.0 / 2.0?
1 and 2 are both int, so the result of 1/2 will be cast (truncated) to an int. 0.5 -> 0.
You need to make sure either of the operands supports decimal points:
double hola = (1.0 / 2) * (double)x.height;
Or:
double hola = ((double)1 / 2) * (double)x.height;
Dividing two integers will performs an integer division, which gives the result also in the same type(fractional part is truncated).Where a non-integer division(here double) on int arguments by explicitly casting at least one of the arguments to a double. So your code will be :
double hola = (1 / (double)2) * (double)x.height;
OR
double hola = ((double)1 / 2) * (double)x.height;

Does Convert.ToSingle() then System.Convert.ToString() cause miscalculation for Double input?

I know title is not clear enough , let me explain. I'm working on a project about an iterative algorithm to compute geodetic coordinates and this is the first part of it. Basically program is doing some iterative calculations using the textbox inputs. It converts textbox input to Single , uses it for calculation and then converts to String as an output. At some points , I need to use an output as an input for following processes but the problem is , it gives slightly different results. For example , these are the results of an online calculator for x,y,z : 1114125.84474949 , -4844708.42056692 , 3982826.71498173 ; and these are mine 1114125.55452963 , 4844708.5526556 , 3982826.75245773. What causes this? (don't mind the missing minus of y). To sum up , i wonder if converting textbox texts to single then , converting them to string cause this error or not. I hope question is clear.
Here is a part of codes which gave the results I mentioned above. More complicated iterations will also work like this , if I can solve the issue.
private void button1_Click_1(object sender, EventArgs e)
{
this.timer1.Start(); // it's beside the point
double fi = Convert.ToSingle(phi.Text);
double lamda = Convert.ToSingle(lambda.Text);//lamda is input, lambda is textbox
double yuk = Convert.ToSingle(height.Text);
double a = Convert.ToSingle(aa.Text);
double b = Convert.ToSingle(bb.Text);
double ec = (Math.Pow(a, 2) - Math.Pow(b, 2)) / Math.Pow(a, 2); //eccentricity
double N = a / Math.Sqrt(1 - ec * Math.Pow(Math.Sin((Math.PI / 180) * fi), 2));
double xx = ((N + yuk) * Math.Cos((Math.PI / 180) * fi) * Math.Cos((Math.PI / 180) * lamda));
double yy = ((N + yuk) * Math.Cos((Math.PI / 180) * fi) * Math.Sin((Math.PI / 180) * lamda));
double zz = ((Math.Pow(b/a,2) * N + yuk) * Math.Sin((Math.PI / 180) * fi));
coorx.Text = System.Convert.ToString(xx); //xx, yy, zz are textboxes
coory.Text = System.Convert.ToString(yy);
coorz.Text = System.Convert.ToString(zz);
}
Computer use binary format in order to store numbers in the memory, in additon there is limited number of bits that are used to store these values (depend on the type which use), therefore conversion of the string value, which is given in decimal number format, and is of single / double type (so real value number) is usally related with degradation of precision.

Cant seem to compute normal distribution

I have interprated the formula in wikipedia in c# code, i do get a nice normal curve, but is it rational to get values that exceeds 1? isnt it suppose to be a distribution function?
this is the C# implementation :
double up = Math.Exp(-Math.Pow(x , 2) / ( 2 * s * s ));
double down = ( s * Math.Sqrt(2 * Math.PI) );
return up / down;
i double checked it several times and it seems fine to me so whats wrong? my implementation or understanding?
for example if we define x=0 and s=0.1 this impl would return 3.989...
A distribution function, a pdf, has the property that its values are >= 0 and the integral of the pdf over -inf to +inf must be 1. But the integrand, that is the pdf, can take any value >= 0, including values greater than 1.
In other words, there is no reason, a priori, to believe that a pdf value > 1 indicates a problem.
You can think about this for the normal curve by considering what reducing the variance means. Smaller variance values concentrate the probability mass in the centre. Given that the total mass is always one, as the mass concentrates in the centre, the peak value must increase. You can see that trend in the graph the you link to.
What you should do is compare the output of your code with known good implementations. For instance, Wolfram Alpha gives the same value as you quote: http://www.wolframalpha.com/input/?i=normal+distribution+pdf+mean%3D0+standard+deviation%3D0.1+x%3D0&x=6&y=7
Do a little more testing of this nature, captured in a unit test, and you will be able to rely on your code with confidence.
Wouldn't you want something more like this?
public static double NormalDistribution(double value)
{
return (1 / Math.Sqrt(2 * Math.PI)) * Math.Exp(-Math.Pow(value, 2) / 2);
}
Yes, it's totally OK; The distribution itself (PDF) can be anything from 0 to +infinity; the thing should be in the range [0..1] is the corresponding integral(s) (e.g. CDF).
You can convince yourself if look at the case of non-random value: if the value is not a random at all and can have only one constant value the distribution degenerates (standard error is zero, mean is the value) into Dirac Delta Function: a peak of infinite hight but of zero width; integral however (CDF) from -infinity to +infinity is 1.
// If you have special functions implemented (i.e. Erf)
// outcoume is in [0..inf) range
public static Double NormalPDF(Double value, Double mean, Double sigma) {
Double v = (value - mean) / sigma;
return Math.Exp(-v * v / 2.0) / (sigma * Math.Sqrt(Math.PI * 2));
}
// outcome is in [0..1] range
public static Double NormalCDF(Double value, Double mean, Double sigma, Boolean isTwoTail) {
if (isTwoTail)
value = 1.0 - (1.0 - value) / 2.0;
//TODO: You should have Erf implemented
return 0.5 + Erf((value - mean) / (Math.Sqrt(2) * sigma)) / 2.0;
}

Categories

Resources