Could you explain the behavior of Ceiling method? [duplicate] - c#

This question already has answers here:
Why is floating point arithmetic in C# imprecise?
(3 answers)
Closed 7 years ago.
I just did a test with LINQPad:
Could you explain me why/how the ceiling method is reacting like this? Notice the 123.12 in the middle.
Math.Ceiling(123.121 * 100) / 100 'display 123.13
Math.Ceiling(123.1200000000001 * 100) / 100 'display 123.13
Math.Ceiling(123.12000000000001 * 100) / 100 'display 123.12
Math.Ceiling(123.12000000000002 * 100) / 100 'display 123.13
I did the test in VB.NET but it should be the same in C#.

This is floating point rounding. C# parses 123.12000000000001 and 123.12 as having the same value. 123.12000000000002 is parsed as the next available double.
var bytes = BitConverter.ToString(BitConverter.GetBytes(123.12));
// outputs 48-E1-7A-14-AE-C7-5E-40
var bytes1 = BitConverter.ToString(BitConverter.GetBytes(123.12000000000001));
// outputs 48-E1-7A-14-AE-C7-5E-40
var bytes2 = BitConverter.ToString(BitConverter.GetBytes(123.12000000000002));
// outputs 49-E1-7A-14-AE-C7-5E-40

Ceiling returns the number passed to it if they are whole numbers, or else the next highest whole number. So 5.0 stays 5.0 but 5.00001 becomes 6.0.
So, of the examples, the following are obvious:
Math.Ceiling(123.121 * 100) / 100 // Obtain 12312.1, next highest is 12313.0, then divide by 100 is 123.13
Math.Ceiling(123.1200000000001 * 100) / 100 // Likewise
Math.Ceiling(123.12000000000002 * 100) / 100 // Likewise
The more confusing one is:
Math.Ceiling(123.12000000000001 * 100) / 100 //display 123.12
However, let's take a look at:
123.12000000000001 * 100 - 12312.0 // returns 0
Compared to:
123.1200000000001 * 100 - 12312.0 // returns 1.09139364212751E-11
123.12000000000002 * 100 - 12312.0 // returns 1.81898940354586E-12
The latter two multiplications have results that are slightly higher than 12312.0, so while (123.12000000000002 * 100).ToString() returns "12312" the actual number produced by 123.12000000000002 * 100 is mathematically 12312.000000000002 the nearest possible double for 123.12000000000002 is is 123.1200000000000181898940354586 so that is what is worked on.
If you are used to only doing decimal arithmetic it may seem strange that 123.12000000000002 is "rounded" to 123.1200000000000181898940354586, but remember that these numbers are stored in terms of binary values, and rounding depends on the base you are working in.
So while the string representation doesn't indicate it, it is indeed slightly higher than 12312 and so its ceiling is 12313.
Meanwhile with 123.12000000000001 * 100, that is mathematically 12312.000000000001 but the nearest possible double to 123.12000000000001 is that it can fit into is 123.12. So that is what is used for the multiplication, and when the result passed to the subsequent call to Ceiling() its result is 12312.

This is due to floating point rounding rather than Math.Ceiling per se which is because floating point values cannot represent all values with 100% accuracy.
Your example is a little contrived anyway because if you try to type 123.12000000000001 in visual studio is changes it to 123.12 because it knows that the value cannot be represented as a double.
Read up on this here: What Every Computer Scientist Should Know About Floating-Point Arithmetic (btw this is not specific to .NET)
To fix your issue you can use a decimal value instead of a double. Math.Ceiling has an overload which accepts a decimal (all of these display 123.13):
Debug.WriteLine(Math.Ceiling(123.121D * 100) / 100)
Debug.WriteLine(Math.Ceiling(123.1200000000001D * 100) / 100)
Debug.WriteLine(Math.Ceiling(123.12000000000001D * 100) / 100)
Debug.WriteLine(Math.Ceiling(123.12000000000002D * 100) / 100)
Whether this fix is appropriate of course depends on what level of accuracy you require.

The Ceiling method returns the next higher integer equivalent.
so Ceiling(123.01) = 124
& Ceiling(123.0) = 123

Related

Trying to Convert Excel Equation to C#

I've got this Excel equation and I'm struggling to convert it into c#.
The "to the power of" and "log" parts are tripping me up.
The excel equation is as follows:
LOG((10^(PreSkillRating/400)/((-ChangeInRating/KFactor)+1)-10^(PreSkillRating/400)))*400/LOG(10)
So far I have this:
Math.Log((Math.Pow(PreSkillRating / 400, 10)) / (((ChangeInRating * -1) / KFactor) + 1) - Math.Pow((PreSkillRating / 400), 10)) * 400 / Math.Log(10)
I'm also aware that I will have to check for 0 when dividing to stop the Attempted to divide by zero error.
For example when I use the following values for each of the variables I get 1879.588002 as the answer in excel but infinity in c#.
PreSkillRating = 1600
ChangeInRating = 50
KFactor = 60
What am I doing wrong?
Based on earler comments and my first answer, let's summarize:
typecast for double division
wrong order of arguments for Pow
wrong method Math.Log(x). You can use Math.Log(x,10) or Math.Log10(x)
Try following implementation:
Math.Log10((Math.Pow(10, (double)PreSkillRating / 400)) / (((ChangeInRating * -1.0) / KFactor) + 1) - Math.Pow(10, (double)PreSkillRating / 400)) * 400 / Math.Log10(10)
Are your variables int values?
Then you have to add a typecast. See Division in C# to get exact value
Otherwise, divisions are performed as integer divisions, which causes rounding operation for each step separately.

Same mathematical expression returns different result

I am baffled by this one.
So I have a console application doing a lot of calculations (trust me, thousands of them ). In one method, I have some parameters that need calculating, in different situations. For one of them, the mathematical expression is basically the same, only one difference in a term. Here is the code snippet along with all the lines between the 2 formulas in question, the Nq1 and Nq2 ones ( first formula of the code and last one to be more easy ):
//drained conditions
Nq1 = Math.Round((Math.Pow(Math.E, Math.PI * Math.Tan(studiu.Fi * Constants.ConversionToDeg)) * Math.Pow((Math.Tan(45 + studiu.Fi / 2.00) * Constants.ConversionToDeg), 2)), 2);
//Combination 2
studiu.Fi = FiAfectat;
//drained conditions
Nq2 = Math.Round((Math.Pow(Math.E, Math.PI * Math.Tan(studiu.Fi * Constants.ConversionToDeg)) * Math.Pow((Math.Tan(45 + studiu.Fi / 2.00) * Constants.ConversionToDeg), 2)), 2);
The first formula returns 18.04 but the second one returns 0.01. How is this possible ? Only the studiu.Fi term differs, but not by that much ( 32 in the first case and 27 in the second ).
How can Nq1 be 18 and Nq2 be 0.01 ? Am I missing something here ?
Tan(x) is a periodic function that will change drastically for a small change in x. Since the two formulas only differ by a term inside the Tan function, this is likely your problem.
Also, it is likely that you should be using radians instead of degrees. If this is the case you should use a command that converts real numbers to radians instead of one that converts to degrees.

C# Math.Ceiling rounds up a event double... (500 to 501) (formula)

why rounds C# this calculation up?
500 -> 501
MessageBox.Show(Math.Ceiling(1 / (4 * 1 - 4 * 0.9) * 200).ToString());
Returns.
---------------------------
---------------------------
501
---------------------------
OK
---------------------------
I dont know why.
Excel doesnt do this, too.
I need the ceiling function for the fomula which returns 500 instead 501.
I could use this instead. But i would know if there is another solution and why C# do this.
MessageBox.Show(Math.Ceiling(Math.Floor((1 / (4 * 1 - 4 * 0.9) * 200) * 100) / 100).ToString());
The problem is that 500 is actually not 500 but rather 500.0000000001 (or something similar) due to using floating point operations.
To solve this issue, use decimal instead of double.
MessageBox.Show(Math.Ceiling(1.0M / (4.0M * 1.0M - 4.0M * 0.9M) * 200.0M).ToString());
See What every Computer Scientist should know about Floating-Point Arithmetic for further information, or take a look at this website for a simpler explanation.

Increment, decrement by percentage

I'll try to explain this problem the best way i can with code:
double power = 5000;
//picked up 5 power ups, now need to increase power by 10% per powerup
power += 5 * (power * .10);
//later on...ran into 5 power downs need to decrease power back to initial hp
power -= 5 * (power * .10);//7500 - 3750 -- doesn't work
So what i need is a scaleable solution that gets back to the original value using only the count. Any ideas?
The best way to do this is using a function. It doesn't have to look exactly like this, but:
class Whatever
{
private double basePower = 5000;
public int numPowerUps = 5;
public double GetActualPower()
{
return basePower + (numPowerUps * basePower * 0.1);
}
}
Just change numPowerUps back to 0 when they run out. This way, it looks a whole lot neater.
An aside:
The reason it's not working is because of the fact that adding and then subtracting percentages doesn't work. For instance:
1. What is 10% of 100? --> 10
2. Add that to the 100 --> 110
3. What is 10% of 110? --> 11
4. Subtract that from 110 --> 99
You'll always end up with 99% of your original value. If you really want to take a shortcut, you could instead do this:
1. What is 10% of 100? --> 10
2. Add that to the 100 --> 110
3. What is (100/11) = 9.09090909...% of 110? --> 10
4. Subtract that from 110 --> 100
But then you're potentially susceptible to floating point errors. The function way of doing it is not only neater and clearer, but potentially less error-prone.
To reverse a %age increase, you must divide by the original %age, not subtract.
i.e.:
100 + 5% = 100 * 1.05 = 105
to reverse it:
105 / 1.05 = 100
The more usual '5% off' formula would instead give you:
105 - 5% = (105 * 0.95) = 99.75
To power up:
power <- power * (1 + count * percent);
eg: 5000 * (1 + 5 * 0.1)
5000 * 1.5
7500
To power back down:
power <- power / (1 + count * percent)
eg: 7500 / (1 + 5 * 0.1)
7500 / 1.5
5000
Let's take a more complicated example, 17 power ups, each giving 3% to an intial 1234 power:
1234 * (1 + 17 * 0.3)
= 1234 * (1 + 5.1)
= 1234 * 6.1
= 7527.4
7527.4 / (1 + 17 * 0.3)
= 7527.4 / (1 + 5.1)
= 7527.4 / 6.1
= 1234
It actually looks pretty simple when you write it out like that.
This doesn't work because the two percentages are not taken from the same number. They're taken from the same variable, but not the same number.
The first time, power * 0.10 is 500, and 5*500=2500 so the power will be 5000+2500=7500. Now, the power is 7500, so power * 0.10 is 750. 5*750 = 3750 and 7500-3750=3750 and not 5000 like you started out with.
So apparently, what you want is not really to in/decrease by a percentage of the current power. Perhaps it would be better to set a base power (let's say 5000) and an actual power. Then when you in/decrease, you use actualPower = actualPower + 5*0.1*basePower; or something. Or you just accept that five power downs after five power ups does not get you back to initial hp.
I'm going to suspect that what you mean by "doesn't work" is that the value for power does not end up to be exactly 3750.
This is due to floating-point rounding errors, as floating point values such as double and float are not able to be represented exact values.
If exact values are needed, then using decimal or int would be a better solution, as they are designed to handle exact values.
Edit The actual issue here is not a floating-point rounding error, but an issue noted in Smashery's answer.

Why Math.Atan(Math.Tan(x)) != x?

If tan(x) = y and atan(y) = x why Math.Atan(Math.Tan(x)) != x?
I´m trying to calculate x in something like:
tan(2/x +3) = 5
so
atan(tan(2/x + 3) = atan(5)
and so on... but I´ve tried this:
double d = Math.Atan(Math.Tan(10));
and d != 10. Why?
The tangent function is periodic with period pi, and is invertible only if you restrict it to a subset of its domain over which it is injective. Usually the choice of such set is the open interval ]-pi/2, pi/2[, hence the arctan function will always return a point in that interval. In your case, 10 = 3*pi + 0.57522... Thus, the arctan of the tangent of 10 will return 0.57522...
Note that the arctan function, defined as above, is injective and defined over all the real numbers, hence the converse of your problem math.tan(math.atan(x)) == x
indeed holds for each x (except for numerical errors).
In order to deal with numerical errors, you should never perform comparisons between the results of floating point computations using == or !=. Use abs(number1 - number2) < epsilon // ==
abs(number1 - number2) >= epsilon // !=
instead, where epsilon is a small positive constant.
A graph might help explain why you are not getting the result you expected.
(source: wolfram.com)
http://mathworld.wolfram.com/Tangent.html
That shows the graph of Tan, but if you imagine reading off a value of x for a given y, (e.g. y = 0) then depending on which "strand" of Tan you read, you will get a different answer (-pi, 0, pi...). That's the point about Arctan(x) having more than one solution.
If arctan was restricted to only one of those strands, e.g. -pi/2 < x < pi/2 then Arctan(tan(x)) will return x providing you have accounted for floating point errors.
EDIT: However, according to http://msdn.microsoft.com/en-us/library/system.math.atan.aspx, the atan method already returns -pi/2 < x < pi/2 or NaN if your input is undefined. So the problem must soley be down to floating point rounding.
EDIT (F.R.): Added figure
I dont know any C#, but maths says that tan is not invertable, only in a small intervall.
e.g. tan(pi) = 0 and tan(0) = 0. When asking for atan(0) it could be 0 or pi (or every multiple of pi), so the result is in the range from -pi/2 .. pi/2.
Even if you start with an x in the invertable range i doesnt has to work, because of rounding errors with the floating points (it has not unlimmited precision).
tan-1(tan(x)) == x for all x in (-PI/2, PI/2).
Because the tangent function is periodic we need to normalize input angle. Math.Atan returns an angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2, so it makes sense to normalize to that range (since it obviously won't anything within that range anyway):
double normalizedAngle = (angle + Math.PI / 2) % Math.PI - Math.PI / 2;
Doubles should be compared with some error margin. But in fact for this case Double.Epsilon is too small and "If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, you must use a value that is greater than the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.)" For instance, Math.Atan(Math.Tan(-0.49999632679501449)) + 0.49999632679501449 will be greater than Double.Epsilon for 1.1235582092889474E+307 times.
It might be helpful if you posted what you are trying to accomplish. I have recollections of discovering trig functions that handled the issue if what quadrant the inputs were in for me when I tried playing with angles, for example.
In general, when you are dealing with floating point numbers, you are dealing with approximations. There are numbers that cannot be represented exactly, and the tan and arctan operations are themselves only approximate.
If you want to compare floating point numbers, you need to ask if they are nearly equal, or equivalently, if the difference is less than some small value, and think carefully what you are doing.
Here is are some FAQS (for c++, but the idea is the same), that talk a bit about some of the oddities of floating point numbers:
FAQ 29.16
FAQ 29.17
FAQ 29.18
Edit: Looking at the other answers, I realise that the main problem is probably that tan isn't invertible, but the approximation issue is worth considering too, whenever you test floating point numbers for equality.
Looking at the .net documentation for Math.Atan, atan produces a value between -π/2 and ≤ π/2, which doesn't include 10. That I think is the usual range for arctan.
double d = Math.Atan(1) * (180 / Math.PI);
so d will be 45 in degrees

Categories

Resources