Running this calculation adds an extra decimal point - c#

I'm running the following calculation and for some reason, an extra decimal is added to the result:
decimal bal = 10.0000
decimal totAmount = 15.0000
decimal payAmount = 3
tempAmount = Math.Ceiling((decimal)(bal / totAmount) * payAmount * 100);
tempAmount = tempAmount / 100;
// tempAmount results in 2.01 as opposed to 2.00
Am I running this incorrectly?

Am I running this incorrectly?
No, but you're introducing an intermediate value (10m/15m) which can't be precisely represented as a decimal. It's being rounded up to 0.6666666666666666666666666667, which then leads to the problem.
If you perform the division as the last operation, you can avoid that, at least in this case:
using System;
decimal bal = 10.0000m;
decimal totAmount = 15.0000m;
decimal payAmount = 3;
decimal tempAmount = Math.Ceiling((bal * payAmount * 100m) / totAmount);
tempAmount = tempAmount / 100;
Console.WriteLine(tempAmount);
That prints out 2 instead of 2.01.

Well, let's have a look at the computations
decimal bal = 10.0000m;
decimal totAmount = 15.0000m;
decimal payAmount = 3m;
decimal tempAmount = Math.Ceiling((decimal)(bal / totAmount) * payAmount * 100);
step by step:
bal / totAmount ==
0.6666666666666666666666666667m (note the last 7 due to rounding)
(decimal)(bal / totAmount) * payAmount ==
2.0000000000000000000000000001m (note the very last 1)
(decimal)(bal / totAmount) * payAmount * 100 ==
200.00000000000000000000000001m (note the very last 1)
Math.Ceiling((decimal)(bal / totAmount) * payAmount * 100) ==
201m
Finally, when you divide 201 by 100 you'll get 2.01

Related

Force Calculation to Round Up in C# [duplicate]

I want to round up double to int.
Eg,
double a=0.4, b=0.5;
I want to change them both to integer.
so that
int aa=0, bb=1;
aa is from a and bb is from b.
Any formula to do that?
Use Math.Ceiling to round up
Math.Ceiling(0.5); // 1
Use Math.Round to just round
Math.Round(0.5, MidpointRounding.AwayFromZero); // 1
And Math.Floor to round down
Math.Floor(0.5); // 0
Check out Math.Round. You can then cast the result to an int.
The .NET framework uses banker's rounding in Math.Round by default. You should use this overload:
Math.Round(0.5d, MidpointRounding.AwayFromZero) //1
Math.Round(0.4d, MidpointRounding.AwayFromZero) //0
Math.Round
Rounds a double-precision floating-point value to the nearest integral value.
Use a function in place of MidpointRounding.AwayFromZero:
myRound(1.11125,4)
Answer:- 1.1114
public static Double myRound(Double Value, int places = 1000)
{
Double myvalue = (Double)Value;
if (places == 1000)
{
if (myvalue - (int)myvalue == 0.5)
{
myvalue = myvalue + 0.1;
return (Double)Math.Round(myvalue);
}
return (Double)Math.Round(myvalue);
places = myvalue.ToString().Substring(myvalue.ToString().IndexOf(".") + 1).Length - 1;
} if ((myvalue * Math.Pow(10, places)) - (int)(myvalue * Math.Pow(10, places)) > 0.49)
{
myvalue = (myvalue * Math.Pow(10, places + 1)) + 1;
myvalue = (myvalue / Math.Pow(10, places + 1));
}
return (Double)Math.Round(myvalue, places);
}
Just some adjusting #BrunoLM's answer with more samples :
Math.Round(0.4); // =0
Math.Round(0.5); // =0
Math.Round(0.6); // =1
Math.Round(0.4, MidpointRounding.AwayFromZero); // = 0
Math.Round(0.5, MidpointRounding.AwayFromZero); // = 1
Math.Round(0.6, MidpointRounding.AwayFromZero); // = 1
Math.Round(0.4, MidpointRounding.ToEven); // = 0
Math.Round(0.5, MidpointRounding.ToEven); // = 0
Math.Round(0.6, MidpointRounding.ToEven); // = 1
Math.Round(0.5) returns zero due to floating point rounding errors, so you'll need to add a rounding error amount to the original value to ensure it doesn't round down, eg.
Console.WriteLine(Math.Round(0.5, 0).ToString()); // outputs 0 (!!)
Console.WriteLine(Math.Round(1.5, 0).ToString()); // outputs 2
Console.WriteLine(Math.Round(0.5 + 0.00000001, 0).ToString()); // outputs 1
Console.WriteLine(Math.Round(1.5 + 0.00000001, 0).ToString()); // outputs 2
Console.ReadKey();
Another option:
string strVal = "32.11"; // will return 33
// string strVal = "32.00" // returns 32
// string strVal = "32.98" // returns 33
string[] valStr = strVal.Split('.');
int32 leftSide = Convert.ToInt32(valStr[0]);
int32 rightSide = Convert.ToInt32(valStr[1]);
if (rightSide > 0)
leftSide = leftSide + 1;
return (leftSide);
It is also possible to round negative integers
// performing d = c * 3/4 where d can be pos or neg
d = ((c * a) + ((c>0? (b>>1):-(b>>1)))) / b;
// explanation:
// 1.) multiply: c * a
// 2.) if c is negative: (c>0? subtract half of the dividend
// (b>>1) is bit shift right = (b/2)
// if c is positive: else add half of the dividend
// 3.) do the division
// on a C51/52 (8bit embedded) or similar like ATmega the below code may execute in approx 12cpu cycles (not tested)
Extended from a tip somewhere else in here. Sorry, missed from where.
/* Example test: integer rounding example including negative*/
#include <stdio.h>
#include <string.h>
int main () {
//rounding negative int
// doing something like d = c * 3/4
int a=3;
int b=4;
int c=-5;
int d;
int s=c;
int e=c+10;
for(int f=s; f<=e; f++) {
printf("%d\t",f);
double cd=f, ad=a, bd=b , dd;
// d = c * 3/4 with double
dd = cd * ad / bd;
printf("%.2f\t",dd);
printf("%.1f\t",dd);
printf("%.0f\t",dd);
// try again with typecast have used that a lot in Borland C++ 35 years ago....... maybe evolution has overtaken it ;) ***
// doing div before mul on purpose
dd =(double)c * ((double)a / (double)b);
printf("%.2f\t",dd);
c=f;
// d = c * 3/4 with integer rounding
d = ((c * a) + ((c>0? (b>>1):-(b>>1)))) / b;
printf("%d\t",d);
puts("");
}
return 0;
}
/* test output
in 2f 1f 0f cast int
-5 -3.75 -3.8 -4 -3.75 -4
-4 -3.00 -3.0 -3 -3.75 -3
-3 -2.25 -2.2 -2 -3.00 -2
-2 -1.50 -1.5 -2 -2.25 -2
-1 -0.75 -0.8 -1 -1.50 -1
0 0.00 0.0 0 -0.75 0
1 0.75 0.8 1 0.00 1
2 1.50 1.5 2 0.75 2
3 2.25 2.2 2 1.50 2
4 3.00 3.0 3 2.25 3
5 3.75 3.8 4 3.00
// by the way evolution:
// Is there any decent small integer library out there for that by now?
It is simple. So follow this code.
decimal d = 10.5;
int roundNumber = (int)Math.Floor(d + 0.5);
Result is 11

For Loop in Foreach Loop Performance Improvement

I have a db table with 2M entries
My XPositions table structure is
Id - int
FID - int
CoordinateQue - int
Latitude - float
Longitude - float
Each row represents a marker position and I need to calculate distance between each coordinates and save to another table.
My xWeights table structure is;
Id - int
x_Id - int
Tox - int
Distance - decimal(18,8)
So far my working code is
var query = _xRepository.TableNoTracking;
var xNodes = query.ToList()
var n = new xWeights();
foreach (var x in xNodes)
{
for (var i = 0; i < xNodes.Count; i++)
{
if(x.Id == xNodes[i].Id)
{
//Do nothing - Same Node
}
else
{
var R = 6378137;
var φ1 = (Math.PI / 180) * x.Latitude;
var φ2 = (Math.PI / 180) * xNodes[i].Latitude;
var Δφ = (xNodes[i].Latitude - x.Latitude) * (Math.PI / 180);
var Δλ = (xNodes[i].Longitude - x.Longitude) * (Math.PI / 180);
var Δψ = Math.Log(Math.Tan(Math.PI / 4 + φ2 / 2) / Math.Tan(Math.PI / 4 + φ1 / 2));
var q = Math.Abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.Cos(φ1); // E-W course creates problem with 0/0
// if Longitude over 180° take shorter rhumb line across the anti-meridian:
if (Math.Abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? -(2 * Math.PI - Δλ) : (2 * Math.PI + Δλ);
var dist = (Math.Sqrt(Δφ * Δφ + q * q * Δλ * Δλ)) * R;
n.x_Id = x.Id;
n.Tox = xNodes[i].Id;
n.Distance = dist;
_xWeightsRepository.Insert(n);
}
}
}
My problem is; I am getting approximately 35k records per minute so will be 2.1M record per hour. This will take forever to finish this. Any ideas how to improve the performance?
The problem is not with this function, but with what you are trying to achieve.
You are trying to insert every from-to combination into _xWeightsRepository. If there are 2 million nodes, then that means 4 thousand billion weights.
If you could insert a weight per CPU clock cycle (which is several orders of magnitude faster than you could ever actually hope to achieve) then you'll still be waiting ten or twenty years.
Check out SQL spatial indexes. I'm going to take a guess that your answer lies in that direction:
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-spatial-index-transact-sql

private double always returning 0

I have this function:
private double getTotal(string str)
{
double total = 0;
byte[] asciiBytes = Encoding.ASCII.GetBytes(str);
foreach(int c in asciiBytes)
{
total = total + c;
total = total * (5 * (c ^ 2) / (c*6));
}
return Math.Round(total);
}
This is used to get a total of a strings ASCII values but does some math along the way rather than just adding. I need this to return the total, but is currently returning 0. How can I make it return the correct value? (PS: It needs to return an integer, but this can be in the datatype of a double for conversion later. Basically just need it to return a whole number.) (PSPS: I don't know what the string will be, it's up to the end user)
_
You probably misunderstood the ^ sign. It stands for a bitwise exclusive or, rather than an exponentiation. If you want to use the latter, use this:
total = total * (5 * (Math.Pow (c, 2) / (c * 6));
However, you could write it shorter/more beautiful/more efficient as well:
total *= (5 * (c * c) / (6 * c));
I replaced the Pow, as it is slower than a simple multiplication and used an assignment-operator.
Furthermore, the equation itself can be simplified:
total *= c * (5 / 6);
However, you should still mark the numbers as doubles, as 5/6 would result in 0 otherwise:
total *= c * (5.0 / 6.0)
For more information on exponentiation in C#, have a look at this.
By the way, the ^ sign takes every bit of the numbers and compares them. The new value will be 1 if the first bit or the second bit, but not both bits are 1.
So for example 0101 xor 1110 would result in 1011.
You have casting problem. The c variable is integer. Your problem is in the total = total * (5 * (c ^ 2) / (c*6)); expression.
Because the internal results (c ^ 2) and (c*6) aren't double, when the division result has floating point such as 0.nnnnn, the final result isn't double and you get only the 0 which is the real part of the number. And the result expression (5 * (c ^ 2) / (c*6)) as an Integer is 0. Finally the expression is as total=total * (0);
Use internal castings in your code
Replace your code with the following :
total = total * (5 * ((double)(c ^ 2)) / ((double)(c * 6)));
Please run the following code
static private double getTotal(string str)
{
double total = 0;
byte[] asciiBytes = Encoding.ASCII.GetBytes(str);
foreach (int c in asciiBytes)
{
double dC = c;
total = total + c;
double cXor2 = c ^ 2;
double c6 = c * 6;
double fiveCXor2 = 5 * cXor2;
double semiFinal = fiveCXor2 / c6;
double final = total * semiFinal;
Console.WriteLine("c = " + (c).ToString());
Console.WriteLine("c ^ 2 = " + (cXor2).ToString());
Console.WriteLine("c * 6 = " + (c6).ToString());
Console.WriteLine("5 * (c ^ 2) = " + (fiveCXor2).ToString());
Console.WriteLine("semi final = " + semiFinal);
Console.WriteLine("final = " + final);
Console.WriteLine("--------------------------------------------");
total = total * (5 * (c ^ 2) / (c * 6));
Console.WriteLine("TOTAL = " + total);
Console.WriteLine("--------------------------------------------");
}
return Math.Round(total);
}
Sample result is :
c = 97
c ^ 2 = 99
c * 6 = 582
5 * (c ^ 2) = 495
semi final = 0.850515463917526
final = 82.5
--------------------------------------------
TOTAL = 0
--------------------------------------------
c = 98
c ^ 2 = 96
c * 6 = 588
5 * (c ^ 2) = 480
semi final = 0.816326530612245
final = 80
--------------------------------------------
TOTAL = 0
--------------------------------------------
As you can see the problem is casting
Because the c variable is int the casting procedure is :
step 1
[double] = [double] * ([int] * ([int] ^ [int] ) / ([int] * [int] ))
total = total * (5 * (c ^ 2 ) / (c * 6 ));
step 2
[double] = [double] * ([int] * ([int]) / ([int] ))
total = total * (5 * (X) / (Y) );
step 3
[double] = [double] * ([int] * [int]))
total = total * (5 * XdivY );
**CASTING PROBLEM : In this step the XdivY is integer and when the result is 0.1234 the INT result is 0**
step 4
[double] = [double] * ([double]))
total = total * (5mulXdivY );
here c# casting the 5mulXdivY 0 to double but the result is zero
step 5
[double] = [double]
total = 0
Problem is with the this line in your code
total = total * (5 * (c ^ 2) / (c*6));
c ^ 2 returns a smaller value than c*6. Now the operator / is integer division so the result of a smallnumber/largenumber will always return zero. This will make the value of variable total zero in every iteration of the loop. Change the code like this and it will give you the result you expect.
private double getTotal(string str)
{
double total = 0;
byte[] asciiBytes = Encoding.ASCII.GetBytes(str);
foreach (int c in asciiBytes)
{
total = total + c;
total = total * (5 * (double)(c ^ 2) / (double)(c * 6));
}
return Math.Round(total);
}
Hope it helps.
Add double to one of the ints
private double getTotal(string str)
{
double total = 0;
byte[] asciiBytes = Encoding.ASCII.GetBytes(str);
foreach (int c in asciiBytes)
{
total = total + c;
total = total * ((double)5 * (c ^ 2) / (c * 6));
}
return Math.Round(total);
}

Angle Normalization C#

I have an Angle class that has this constructor
public Angle(int deg, // Degrees, minutes, seconds
int min, // (Signs should agree
int sec) // for conventional notation.)
{
/* //Bug degree normalization
while (deg <= -180) deg += 360;
while (deg > Math.PI) deg -= 360;
//correction end */
double seconds = sec + 60 * (min + 60 * deg);
value = seconds * Math.PI / 648000.0;
normalize();
}
and I have these values for testing that constructor
int[] degrees = { 0, 180, -180, Int32.MinValue / 60, 120+180*200000};
int[] minutes = { 0, 0, 0, 0,56};
int[] seconds = { 0, 0, 0, 0,10};
Console.WriteLine("Testing constructor Angle(int deg, int min)");
for (int i = 0; i < degrees.Length; i++)
{
p = new Angle(degrees[i], minutes[i], seconds[i]);
Console.WriteLine("p = " + p);
}
/*Testing constructor Angle(int deg, int min)
p = 0°0'0"
p = 180°0'0"
p = 180°0'0"
p = 0°8'0" incorrect output
p = -73°11'50" incorrect output expected 120 56 10
*/
I do not understand why there is a bug here ? and why did they use divide Int32.MinValue by 60 and 120+180*200000 as this format ?
the comments in the constructor is a correction for the code
UPDATE: Added the code of normalize()
// For compatibility with the math libraries and other software
// range is (-pi,pi] not [0,2pi), enforced by the following function:
void normalize()
{
double twoPi = Math.PI + Math.PI;
while (value <= -Math.PI) value += twoPi;
while (value > Math.PI) value -= twoPi;
}
The problem is in this piece of code:
double seconds = sec + 60 * (min + 60 * deg);
Although you are storing seconds as a double, the conversion from int to double is taking place after sec + 60 * (min + 60 * deg) is computed as an int.
The compiler will not choose double arithmetics for you based on the type you decide to store the result in. The compiler will choose the best operator overload based on the types of the operands which in this case are all int and look for a valid implicit conversion (in this case int to double) afterwards; therefore it is choosing int arithmetics and the operation will overflow in the last two test cases:
Int32.MinValue / 60 * 60 * 60 = Int32.MinValue * 60 < Int32.MinValue which will overflow.
120 + 180 * 200000 * 60 * 60 > Int32.MaxValue which will also overflow.
Your expected results for these two cases are probably not considering this behavior.
In order to solve this issue, change your code to:
double seconds = sec + 60 * (min + 60f * deg);
Explicitly setting 60 to a double typed literal constant (60f) will force the compiler to resolve all operations to double arithmetics.
Also, it is worth pointing out that your constructor logic has some other issues:
You should be validating the input data; should it be valid to specify negative minutes or seconds? IMO that doesn't seem reasonable. Only deg should be allowed to have a negative value. You should check for this condition and act accordingly: throw an exception (preferable) or normalize sign of min and sec based on the sign of deg (ugly and potentially confusing).
Your seconds calculation doesn't seem to be correct for negative angles (again, this is tied to the previous issue and whatever sign convention you have decided to implement). Unless the convention is that negative angles must have negative deg, min and sec, the way you are computing seconds is wrong because you are always adding the minutes and seconds terms no matter the sign of deg.
UPDATE There is one more issue in your code that I missed until I had the chance to test it. Some of your test cases are failing because double doesn't have enough resolution. I think your code needs some major refactoring; normalize() should be called first. This way you will always be managing tightly bounded values that can not cause overflows or precision loss.
This is the way I would do it:
public Angle(int deg, int min, int sec)
{
//Omitting input values check.
double seconds = sec + 60 * (min + 60 * normalize(deg));
value = seconds * Math.PI / 648000f;
}
private int normalize(int deg)
{
int normalizedDeg = deg % 360;
if (normalizedDeg <= -180)
normalizedDeg += 360;
else if (normalizedDeg > 180)
normalizedDeg -= 360;
return normalizedDeg;
}
// For compatibility with the math libraries and other software
// range is (-pi,pi] not [0,2pi), enforced by the following function:
void normalize()
{double twoPi = Math.PI + Math.PI;
while (value <= -Math.PI) value += twoPi;
while (value > Math.PI) value -= twoPi;
}
This is the normalize function that I have
while loops is generally a bad idea. If you deal with small values it's okay, but imagine you have some angle like 1e+25, that'd be 1.59e+24 iterations or about 100 million years to compute if you have a decent CPU.
How it should be done instead:
static double NormalizeDegree360(double value)
{
var result = value % 360.0;
return result > 0 ? result : result + 360;
}
static double NormalizeDegree180(double value)
{
return (((value + 180) % 360) + 360) % 360 - 180;
}
static double TwoPI = 2*System.Math.PI;
static double NormalizeRadians2Pi(double value)
{
var result = value % TwoPI;
return result > 0 ? result : result + TwoPI;
}
static double NormalizeRadiansPi(double value)
{
return (((value + System.Math.PI) % TwoPI) + TwoPI) % TwoPI - System.Math.PI;
}
They're using the very large negative and positive numbers to make sure that the normalization caps angles to the range [-180, 180] degrees properly.

Round to 1 decimal place in C#

I would like to round my answer 1 decimal place. for example: 6.7, 7.3, etc.
But when I use Math.round, the answer always come up with no decimal places. For example: 6, 7
Here is the code that I used:
int [] nbOfNumber = new int[ratingListBox.Items.Count];
int sumInt = 0;
double averagesDoubles;
for (int g = 0; g < nbOfNumber.Length; g++)
{
nbOfNumber[g] = int.Parse(ratingListBox.Items[g].Text);
}
for (int h = 0; h < nbOfNumber.Length; h++)
{
sumInt += nbOfNumber[h];
}
averagesDoubles = (sumInt / ratingListBox.Items.Count);
averagesDoubles = Math.Round(averagesDoubles, 2);
averageRatingTextBox.Text = averagesDoubles.ToString();
You're dividing by an int, it wil give an int as result. (which makes 13 / 7 = 1)
Try casting it to a floating point first:
averagesDoubles = (sumInt / (double)ratingListBox.Items.Count);
The averagesDoubles = Math.Round(averagesDoubles, 2); is reponsible for rounding the double value. It will round, 5.976 to 5.98, but this doesn't affect the presentation of the value.
The ToString() is responsible for the presentation of decimals.
Try :
averagesDoubles.ToString("0.0");
Do verify that averagesDoubles is either double or decimal as per the definition of Math.Round and combine these two lines :
averagesDoubles = (sumInt / ratingListBox.Items.Count);
averagesDoubles = Math.Round(averagesDoubles, 2);
TO :
averagesDoubles = Math.Round((sumInt / ratingListBox.Items.Count),2);
2 in the above case represents the number of decimals you want to round upto. Check the link above for more reference.
int division will always ignore fraction
(sumInt / ratingListBox.Items.Count);
here sumint is int and ratingListBox.Items.Count is also int , so divison never results in fraction
to get the value in fraction , you need to datatype like float and type cast the sumInt and count to float and double and then use divison
var val= Math.Ceiling(100.10m);
result 101

Categories

Resources