Calculate Percent Increase and Decrease
Asked Answered
D

3

7

I'm likely over-thinking this, but I'm looking for something a bit more elegant than what I currently have.

I have this method called CalculatePercentageTrend, which takes in a long "previous" value and "current" value. As it currently stands :

public static string CalculatePercentageTrend(long previousValue, long currentValue)
{
    var trend = "0.00%";

    // calculate percentage increase
    if (previousValue < currentValue)
    {
        if (previousValue != 0)
        {
            var difference = previousValue - currentValue;
            var pctDecrease = (double)(difference / previousValue) * 100;
            trend = pctDecrease.ToString("P");
        }
        else
        {
            trend = currentValue.ToString("P");
        }
     }
    // calculate percentage decrease
    else if (currentValue < previousValue)
    {
        if (previousValue != 0)
        {
            var difference = currentValue - previousValue;
            var pctIncrease = (double)(difference / previousValue) * 100;
            trend = pctIncrease.ToString("P");
        }
        else
        {
            trend = currentValue.ToString("P");
        }
    }
    return trend;
}

This feels very repetitive, and has a few short comings. Negative trends aren't calculated properly, as they always result in a 0.00% change - what would be great is if I could get a negative percentage IF in fact the previous value is greater than the current value.

Also, I'm handling any possible 0's before dividing, but I'm wondering if there's a better approach to that as well.

My Question :

How can I get negative percentages to calculate correctly, and how can I improve this code overall?

Duomo answered 25/11, 2014 at 19:49 Comment(3)
You need to get rid of your first if else if. Simply check if either variable is 0. Then do (previous - current) / previous * 100. This will return positive if it is an increase and negative if it is a decrease.Grillroom
At least, change (double)(difference / previousValue) * 100 to ((double) difference / previousValue) * 100;Maxwell
Important to understand that you should NOT multiply this result by 100 if you intend to use it in any fashion other than displaying a more 'friendly' value to the user. 31% == .31, 31% != 31. 31 == 3100%.Folacin
F
21

You're way over thinking it.

double CalculateChange(long previous, long current)
{
    if (previous == 0)
        throw new InvalidOperationException();

    var change = current - previous;
    return (double)change / previous;
}

To display this data to a user in a friendly 'percentage' format, you can use:

string DoubleToPercentageString(double d)
{
    return "%" + (Math.Round(d, 2) * 100).ToString();
}
Folacin answered 25/11, 2014 at 19:54 Comment(17)
you need to multiply the result by 100 to get the number as a percentageSledge
That is strictly a display concern. If you multiply this result by 100 and then try to use it to operate on anything, you will not get the desired result. 50% == .5Folacin
Yes, but his method indicates the answer will be returned as a percentage not a decimal.Sledge
Again, 50% equals .5, not 50.Folacin
1 meter == 100 cm, yet if a function was named GetMeters I would expect it to return 1 not 100Sledge
What??? Look the original method was named 'CalculatePercentageTrend' and its return type is a string, so I'm not putting a lot of stock into the original design. That doesn't even make any sense. If I called this function to perform some sort of mathematical operation and it returned the value multiplied by 100, I would trash the entire library immediately.Folacin
Also, this is pretty basic mathematics. Open Excel, put a value of '.5' into a cell, then change the formatting to Percentage. Let me know what you get.Folacin
you are not the designer, excel also stores dates as numbers. That is an implementation detail, and shouldn't factor into this discussion. 0.5 == 50% sure, the method name includes Percent, which is the decimal representation multiplied by 100.Sledge
Let us continue this discussion in chat.Sledge
I deleted my last comment, as it was inappropriate and not constructive. Just consider this: p = 1; c = 1; x = GetPercChange(p, c); y = p * x; y != x, if you multiply by 100 with the function.Folacin
those are equal... p = 1; c = 1; x = GetPercChange(p,c) = 0, y = p * x = 0... 100 * 0 = 0Sledge
Hah. Of course I choose the corner case in my haste. Try p = 2.Folacin
this should be in chat, want to clear the air there?Sledge
(13,11) => -15.38 %, your result => %-15Selfpreservation
If you want to display the fractional result of the %, change the 2 to a 4 when you call Math.Round in the ToString method.Folacin
@JamesRalston I know this is ancient history, but I stumbled across this question and immediately recollected this conversation. Thought you might be interested in how MS views the issue: #1791475Folacin
I have never seen a program return an integer for a percent. It's always a decimal with 1 representing 100. I know these comments are dated, but I'm a bit blown away that anyone would expect a percent to be returned as 50 rather than .50.Incommodious
E
6

I used following function with 100% accuracy.

    decimal CalculateChange(decimal previous, decimal current)
    {
        if (previous == 0)
            return 0;

        if (current == 0)
            return -100;

        var change = ((current - previous) / previous) * 100;

        return change;
    }
Einhorn answered 29/5, 2018 at 10:46 Comment(2)
This is exactly what I need! Works like a charm!Puttier
if the previous is 0 then the changes is 100% not 0 if 0 becomes any number then there is a change by 100%Stateroom
S
4

I think this will resolve your issue, all you need is calculating the difference and it's ratio with respect to previous value.

public static string CalculatePercentageTrend(long previous, long current)
{
    return ((current - previous) / (double)previous).ToString("p");
}
Selfpreservation answered 25/11, 2014 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.