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.
Related
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.
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
First of all pardon me to raise this question here (not sure). Not good in maths so need help from others to understand how to calculate.
I have to calculate proportional ratio score. For doing that i am taking two input values
ValueA = 3
ValueB = 344.
To find the percentage of the proportional ratio ((ValueB-ValueA)/ValueA )*100)
that formula gives me the score 11366.6.
Now i have to match with proportional percentage against with following table,
no idea how to match with percentage
for example the score comes around 43.12 % then i will pick the value 5 (>40 -50)
% Ratio Score
0 0
≤10 1
>10 – 20 2
>20 – 30 3
>30 – 40 4
>40 – 50 5
>50 – 60 6
>60 – 70 7
>70 – 80 8
>80 – 90 9
>90 – 100 10
your formula is of (as you can see by the 11366.6 percentage) - it should be
100.0*(ValueB-ValueA)/(double)ValueB
this will give you values in between 0 and 100 percent if ValueB is always bigger than ValueA (if not use):
100.0*Math.Abs(ValueB - ValueA)/(double)Math.Max(ValueA, ValueB)
based on the table your score should than be simply:
var score = (int)Math.Ceiling(percentage / 10.0)
You should swap value a and value b of you get percentages bigger than 100. By the way, finding the proportional value is not unique and the formula you have provided is one way to do that. I guess Valuea/valueb is also a possibility for example.
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.
There are the obvious quirks of Math.Round but is there a way to make Math.Round fulfill this type of manipulation for a rating system.
If greater than .0 and less than or equal to .5, display half a star
If greater than .5 and less than or equal to .0 display whole star
So obviously a half star would be .5 and a whole start would be the next whole value.
I don't know of a rounding method to go to half whole numbers.
Should I just write if statements to control my rounding?
**Edit/Solution
From the below answer I came up with.
double roundedRating = (Math.Ceiling(2 * currentRating)) / 2;
I'd recommend multiplying by two, performing Math.Ceiling, and dividing by two to get to the nearest half.
You're going to want to make sure that you end up performing your checks against integers, rather than floating point numbers.
Start by multiplying the number by 2. Continue doing this until it's an integer value (no value in the decimal part).
Now, continuously divide by 2 until you end up with a number that's less than or equal to the original number. If the result decimal part is greater than .0 and less than or equal to .5, display half a star. If it's greater than .5 and less than or equal to +.0, display a whole star.
Actually, go with matt's answer. ; )
Can this work?
Multiply the number by 10 e.g. 0.1x10, 0.2x10 to get n
Math.Ceil(n / 5) / 2
where n = 1, 2, 3 instead of - .1, .2, .3
examples:
1,2,3,4,5 = 1/2 = 0.5
6,7,8,9,10 = 2/2 = 1
11,12,13,14,15 = 3/2 = 1.5
If efficiency is no issue, the following approach could be used:
Number *= 10;
Number % 10 = remainder;
if(remainder <=5 && remainder != 0)
//Half star
else
//Whole star
However, that code is kinda ugly, but I think it gives you the general idea.
double roundedRating = (int)(currentRating * 2D + 0.999999999999999) / 2D