Rounding datetime in c# - why does this solution work? - c#

I want to round datetime objects in C# to the nearest minute. So if the datetime object has 1 minute, 29 seconds and 999 miliseconds it should be rounded to 1 minute. If it has 1 minute, 30 seconds and 0 miliseconds, it should be rounded to 2 minutes.
I implemented this solution which I found from internet:
var timespan = new TimeSpan(0, 0, 1, 0);
//timespan has a value of 1 minute because I want to round to the nearest minute
return new DateTime(((dateTime.Ticks + timespan.Ticks/2)/ timespan.Ticks)* timespan.Ticks);
I'm not sure about the last line. Why do we divide by 2? Why do we divide with timespan.Ticks and then multiply?

It's a lot used method of rounding to add half of desired precision and then cut the decimals. Examples:
Desired precision: 0.1 ( / 2 -> add 0.05 )
1.44 + 0.05 = 1.49 -> cut last -> 1.4
1.46 + 0.05 = 1.51 -> cut last -> 1.5
Desired precision: 0.01 ( / 2 -> add 0.005 )
1.443 + 0.005 = 1.448 -> cut last -> 1.44
1.465 + 0.005 = 1.470 -> cut last -> 1.47
Desired precision: 1minute -> add 0.5min or 30s or 30000 ticks* (assumed 1s = 1000 ticks). Unit doesn't matter as long as you keep units the same in all calculations.
1min 25s + 30s = 1min 55s -> cut -> 1min
1min 35s + 30s = 2min 05s -> cut -> 2min
So with datetime it's the same. In order to round by minute, you may add half a minute and then cut the rest away.
Ticks are one way of representing time. So just take the tick-count of a minute (new TimeSpan(0, 0, 1, 0)), divide it by two to get the half way and then add it to your time.
Now all you need to do is to cut the rest away and you can do it by diving your time with the precision (tick count of 1 minute). When you divide by 1 minute, everything that's smaller than a minute will be in decimals. Tick is an integer so it "forgets" all the decimals, thus cutting them away. Now all you need to do is make the value valid again by multiplying it with the same value you divided it with. Decimals are already lost at this point, so... TADAA: Rounded by minute.

Related

How do I truncate milliseconds off "Ticks" without converting to datetime?

I have two times in Ticks like so:
//2016-01-22​T17:34:52.648Z
var tick1 = 635890808926480754;
//2016-01-22​T17:34:52.000Z
var tick2 = 635890808920000000;
Now as you can see comparing these two numbers tick1 == tick2 returns false
although the dates are the same (apart from milliseconds).
I would like to truncate the milliseconds off these numbers without converting it to a datetime (because this would reduce efficiency)
I have looked at Math.Round which says:
Rounds a value to the nearest integer or to the specified number of fractional digits.
and also Math.Truncate neither of which I think do what I need.
Looking at Datetime.Ticks it says:
A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond, or 10 million ticks in a second.
Therefore I need to round the number down to the nearest ten million.
Is this possible?
You could use integer division:
if (tick1 / TimeSpan.TicksPerSecond == tick2 / TimeSpan.TicksPerSecond)
This works because if you divide a long/int by a long/int the result is also a long/int therefore truncating the decimal portion.
You can use this:
if(Math.Abs(tick1 - tick2) < TimeSpan.TicksPerSecond)
Which avoid doing divisions.
You may adjust the precision you need with any of the following:
TimeSpan.TicksPerDay
TimeSpan.TicksPerHour
TimeSpan.TicksPerMinute
TimeSpan.TicksPerSecond
TimeSpan.TicksPerMillisecond
Divide it by 1000 like this:
Long Seconds = 635890808926480754/1000
//Seconds = 635890808926480

Stopwatch.ElapsedMillisecond Explanation

I need help with how this works. Microsoft's site doesn't have an answer so I was hoping there is a way to deal with this.
What would
stopwatch.ElapsedMilliseconds % 120000 == 0
mean?
It is in an if statement but I need to understand what is meant by that. Does it mean every 120 seconds (2 minutes), it will equal 0 and restart?
1000 milliseconds = 1 second and therefore 120 000 milliseconds = 2 minutes
% (Mod operator), means it returns the remainder of stopwatch.ElapsedMilliseconds divided by 120000.
== 0 means you're checking for a remainder of zero.
So therefor your statement is true when elapsed time is exactly divisible by 2 minutes (i.e. exactly 2 mins, 4 mins, 6 mins etc.), otherwise it will return false
The Modulus Operator (%) operator computes the remainder after dividing its first operand by its second. All numeric types have predefined remainder operators.
stopwatch.ElapsedMilliseconds % 120000 == 0 means, ElapsedMilliseconds is multiplications of 120000 (which is 2 minutes). So, your if condition hits for every 2 mins.
stopwatch.ElapsedMilliseconds % 120000 == 0
this statement is checking condition on milisecond elapsed in stopwatch and is it completely divisible by 120000, if it is then ok else there might be some other condition also

how to calculate proportional ratio

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.

X-axis scaling from number value to time in C#

I have a line graph where my x-axis shows ticks every 3000 data points. The frequency that I am working with is 10 Hz meaning 3000 data points translates to 3000/10 (300 seconds). Ultimately I would want to show it in minutes, so it should show 300/60 (5 minutes), 10, 15, 20, 25, and so on. Right now I am trying to find it on the properties of the chart but can't find it.
Do this:
var ticks = 3000;
var seconds = ticks / 10;
var minutes = TimeSpan.FromSeconds(seconds).TotalMinutes;

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.

Categories

Resources