Why am I losing information? [duplicate] - c#

This question already has answers here:
Why is this simple calculation of two doubles inaccurate? [duplicate]
(2 answers)
Closed 2 years ago.
I was coding when I found it:
double v = 555.55;
int v2 = (int)Math.Floor(v / 100.0);
double v3 = v2 * 100;
double v4 = v - v2 * 100;
double v5 = v - v3;
At the end, the code says:
v = 555.55
v2 = 5
v3 = 500
v4 = 55.549999999999955
v5 = 55.549999999999955
So, why is that happening and how could I fix it?

Well, fractional part of double v = 555.55 (which is double v = 0.55) is a periodic binary fraction which
can't be represented exactly. What options do we have? Let's make a smallest possible change of 555.55
(i.e. we change the last bit of 555.55):
double v = 555.55;
int v2 = (int)Math.Floor(v / 100.0);
double v4 = v - v2 * 100;
// Here we increment the last bit of v (minimum possible change)
byte[] bytes = BitConverter.GetBytes(v);
bytes[0] = (byte) (bytes[0] + 1);
double d = BitConverter.ToDouble(bytes);
double dv = d - v2 * 100;
string result = string.Join(Environment.NewLine,
$" v = {v:R}",
$"v4 = {v4:R}",
$" v' = {d:R}",
$"v4' = {dv:R}");
Console.Write(result);
Outcome:
v = 555.55
v4 = 55.549999999999955
v' = 555.5500000000001
v4' = 55.55000000000007
So the only options we have either 55.549999999999955 or 55.55000000000007; please, note, that 55.549999999999955 is a better one (4.5e-14 < 7.0e-14).
How to fix it? As you can see, floating point arithmetics brings rounding error; often we can change double into decimal which is standard practice if 555.55 is some kind of currency value (say, dollars and cents):
decimal v = 555.55m;
int v2 = (int)Math.Floor(v / 100m);
decimal v3 = v2 * 100;
decimal v4 = v - v2 * 100;
decimal v5 = v - v3;
string result = string.Join(Environment.NewLine,
$" v = {v}",
$"v2 = {v2}",
$"v3 = {v3}",
$"v4 = {v4}",
$"v5 = {v5}");
Console.Write(result);
Outcome:
v = 555.55
v2 = 5
v3 = 500
v4 = 55.55
v5 = 55.55

Well, by turning a double into an int, you are going to lose information, because an int is a whole number, so everything after the decimal point gets cut away.

Related

c# Pow/Datatype Conversion

I'm fairly new to programming in general. I have read through a raft of posts regarding casting datatype when using the POW function and have tried to take note.
I can't understand why the following isn't working. In the debugger y is getting assigned to 0. I can't understand why this is. I think it is something to do with how I am converting Doubles/Decimals?
public decimal MonthsPayment(){
decimal monthlyint = this.Rate / 12;
int totalpayments = this.Term * 12;
decimal x = monthlyint * this.Amount;
decimal s1 = (1+monthlyint);
decimal s2 = (-totalpayments);
decimal y = (decimal)(Math.Pow((double)s1,(double)s2));
decimal Repayment = x /(1 - y);
return Repayment;

How to calculate the points between two given points and given distance?

I have point A (35.163 , 128.001) and point B (36.573 , 128.707)
I need to calculate the points lies within point A and point B
using the standard distance formula between 2 points, I found D = 266.3
each of the points lies within the line AB (the black point p1, p2, ... p8) are separated with equal distance of d = D / 8 = 33.3
How could I calculate the X and Y for p1 , p2, ... p8?
example of Java or C# language are welcomed
or just point me a formula or method will do.
Thank you.
**The above calculation is actually used to calculate the dummy point for shaded level in my map and working for shaded area interpolation purpose*
that's easy but you need some math knowledge.
PointF pointA, pointB;
var diff_X = pointB.X - pointA.X;
var diff_Y = pointB.Y - pointA.Y;
int pointNum = 8;
var interval_X = diff_X / (pointNum + 1);
var interval_Y = diff_Y / (pointNum + 1);
List<PointF> pointList = new List<PointF>();
for (int i = 1; i <= pointNum; i++)
{
pointList.Add(new PointF(pointA.X + interval_X * i, pointA.Y + interval_Y*i));
}
Straitforward trigonometric solution could be something like that:
// I've used Tupple<Double, Double> to represent a point;
// You, probably have your own type for it
public static IList<Tuple<Double, Double>> SplitLine(
Tuple<Double, Double> a,
Tuple<Double, Double> b,
int count) {
count = count + 1;
Double d = Math.Sqrt((a.Item1 - b.Item1) * (a.Item1 - b.Item1) + (a.Item2 - b.Item2) * (a.Item2 - b.Item2)) / count;
Double fi = Math.Atan2(b.Item2 - a.Item2, b.Item1 - a.Item1);
List<Tuple<Double, Double>> points = new List<Tuple<Double, Double>>(count + 1);
for (int i = 0; i <= count; ++i)
points.Add(new Tuple<Double, Double>(a.Item1 + i * d * Math.Cos(fi), a.Item2 + i * d * Math.Sin(fi)));
return points;
}
...
IList<Tuple<Double, Double>> points = SplitLine(
new Tuple<Double, Double>(35.163, 128.001),
new Tuple<Double, Double>(36.573, 128.707),
8);
Outcome (points):
(35,163, 128,001) // <- Initial point A
(35,3196666666667, 128,079444444444)
(35,4763333333333, 128,157888888889)
(35,633, 128,236333333333)
(35,7896666666667, 128,314777777778)
(35,9463333333333, 128,393222222222)
(36,103, 128,471666666667)
(36,2596666666667, 128,550111111111)
(36,4163333333333, 128,628555555556)
(36,573, 128,707) // <- Final point B
Subtract A from B, component-wise, to get the vector from A to B. Multiply that vector by the desired step value and add it to A. (Note that with eight intermediate steps as you've illustrated, the step distance is 1.0 / 9.0.) Something like this, assuming you really want seven points:
vec2 A = vec2 (35.163, 128.001);
vec2 B = vec2 (36.573, 128.707);
vec2 V = B - A;
for (i = 1; i < 8; i++) {
vec2 p[i] = A + V * (float)i / 8.0;
}
(Sorry, don't know any Java or C#.)
let A be point (xa, ya), and B be point (xb, yb)
alpha = tan-1((yb - ya)/(xb - xa))
p1 = (xa + d * cos(alpha), ya + d * sin(alpha))
pk = (xa + kd * cos(alpha), ya + kd * sin(alpha)), k = 1 to 7
(An equivalent way would be to use vector arithmetic)
At first find the slope of AB line. Get help and formula from here: http://www.purplemath.com/modules/slope.htm
Then consider a triangle of Ap1E(think there is a point E which is right to A and below to p1).
You already know the angle AEp1 is 90degree. and you have calculated angle p1AE(from the slope of AB).
Now find AE and Ep1.
Xp1=Xa+AE and Yp1=Ya+Ep1
This will not be very difficult in C# or java.
Once you understand the logic, you will find pleasure implementing on your own way.

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

Which way is more accurate?

I need to divide a numeric range to some segments that have same length. But I can't decide which way is more accurate. For example:
double r1 = 100.0, r2 = 1000.0, r = r2 - r1;
int n = 30;
double[] position = new double[n];
for (int i = 0; i < n; i++)
{
position[i] = r1 + (double)i / n * r;
// position[i] = r1 + i * r / n;
}
It's about (double)int1 / int2 * double or int1 * double / int2. Which way is more accurate? Which way should I use?
Update
The following code will show the difference:
double r1 = 1000.0, r2 = 100000.0, r = r2 - r1;
int n = 300;
double[] position = new double[n];
for (int i = 0; i < n; i++)
{
double v1 = r1 + (double)i / n * r;
double v2 = position[i] = r1 + i * r / n;
if (v1 != v2)
{
Console.WriteLine(v2 - v1);
}
}
Disclaimer: All numbers I am going to give as examples are not exact, but show the principle of what's happening behind the scenes.
Let's examine two cases:
(1) int1 = 1000, int2= 3, double = 3.0
The first method will give you: (1000.0 / 3) * 3 == 333.33333 * 3.0 == 999.999...
While the second will give (1000 * 3.0) / 3 == 3000 / 3 == 1000
In this scenario - the second method is more accurate.
(2) int1 = 2, int2 = 2, double = Double.MAX_VALUE
The first will yield (2.0 / 2) * Double.MAX_VALUE == 1 * Double.MAX_VALUE == Double.MAX_VALUE
While the second will give (2 * Double.MAX_VALUE) / 2 - which will cause (in Java) to be Infinity, I am not sure what the double standard says about this cases, if it might overflow or is it always infinity - but it is definetly an issue.
So, in this case - the first method is more accurate.
The things might go more complicated if the integers are longs or the double is float, since there are long values that cannot be represented by doubles, so loss of accuracy might happen for large double values in this case, and in any case - large double values are less accurate.
Conclusion: Which is better is domain specific. In some cases the first method should be better and in some the first. It really depends on the values of int1,int2, and double.
However- AFAIK, the general rule of thumb with double precision ops is keep the calculations as small as possible (Don't create huge numbers and then decrease them back, keep them small as longest as you can). This issue is known as loss of significant digits.
Neither is particularly faster, since the compiler or the JIT process may reorder the operation for efficiency anyway.
Maybe I misunderstand your requirement but why do any division/multiplication inside the loop at all? Maybe this would get the same results:
decimal r1 = 100.0m, r2 = 1000.0m, r = r2 - r1;
int n = 30;
decimal[] position = new double[n];
decimal diff = r / n;
decimal current = r1;
for (int i = 0; i < n; i++)
{
position[i] = current;
current += diff;
}

.NET math calculation performances

I asked a question about having the Excel's BetaInv function ported to .NET: BetaInv function in SQL Server
now I managed to write that function in pure dependency less C# code and I do get the same results than in MS Excel up to 6 or 7 digits after comma, results are fine for us, the problem is that such code is embedded in a SQL CLR Function and gets called thousands of time from a stored procedure and makes the execution of the whole procedure about 50% slower, from 30 seconds up to a minute if I use that function or not.
here some code of it, I am not asking a deep analysis but is there anybody who sees any major performance issue in the way I am doing this calculations? like for example usage of other data types instead of doubles or whatsoever... ?
private static double betacf(double a, double b, double x)
{
int m, m2;
double aa, c, d, del, h, qab, qam, qap;
qab = a + b;
qap = a + 1.0;
qam = a - 1.0;
c = 1.0; // First step of Lentz’s method.
d = 1.0 - qab * x / qap;
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
d = 1.0 / d;
h = d;
for (m = 1; m <= MAXIT; ++m)
{
m2 = 2 * m;
aa = m * (b - m) * x / ((qam + m2) * (a + m2));
d = 1.0 + aa * d; //One step (the even one) of the recurrence.
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
h *= d * c;
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
d = 1.0 + aa * d; // Next step of the recurrence (the odd one).
if (System.Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
del = d * c;
h *= del;
if (System.Math.Abs(del - 1.0) < EPS)
{
// Are we done?
break;
}
}
if (m > MAXIT)
{
return 0;
}
else
{
return h;
}
}
private static double gammln(double xx)
{
double x, y, tmp, ser;
double[] cof = new double[] { 76.180091729471457, -86.505320329416776, 24.014098240830911, -1.231739572450155, 0.001208650973866179, -0.000005395239384953 };
y = xx;
x = xx;
tmp = x + 5.5;
tmp -= (x + 0.5) * System.Math.Log(tmp);
ser = 1.0000000001900149;
for (int j = 0; j <= 5; ++j)
{
y += 1;
ser += cof[j] / y;
}
return -tmp + System.Math.Log(2.5066282746310007 * ser / x);
}
The only thing that stands out for me, and is usually a performance hit, is memory allocation. I don't know how often gammln is called but you might want to move the double[] cof = new double[] {} to a static one time only allocation.
double is usually the best type. Especially since the functions in Math take doubles. Unfortunately I see no obvious improvements to make on your code.
It might be possible to use look up tables to get a better first estimate on which you iterate, but since I don't know the Math behind what you're doing I don't know if that's possible in this specific case.
Obviously larger epsilons will improve performance. So choose it as large as possible while fulfilling your accuracy demands.
If the function gets called repeatedly with the same parameters you might be able to cache results.
One thing that looks odd is the way you force small values for c, d,... to FPMIN. My instinct is that this might lead to suboptimal step sizes.
All I've got is unrolling the j loop in gammln, but it'll make at most a tiny difference.
A more radical thought would be to rewrite in pure T-SQL, since it has everything you use: + - * / abs log are all available.

Categories

Resources