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?

Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74
X3074861X
  • 3,709
  • 5
  • 32
  • 45
  • 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. – deathismyfriend Nov 25 '14 at 19:54
  • At least, change `(double)(difference / previousValue) * 100` to `((double) difference / previousValue) * 100;` – Sjips Nov 25 '14 at 19:58
  • 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%. – Michael Nov 25 '14 at 20:01

3 Answers3

20

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();
}
Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74
Michael
  • 1,803
  • 1
  • 17
  • 26
  • you need to multiply the result by 100 to get the number as a percentage – James Ralston Nov 25 '14 at 19:58
  • 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% == .5 – Michael Nov 25 '14 at 19:59
  • Yes, but his method indicates the answer will be returned as a percentage not a decimal. – James Ralston Nov 25 '14 at 20:04
  • 1
    Again, 50% equals .5, not 50. – Michael Nov 25 '14 at 20:07
  • 1 meter == 100 cm, yet if a function was named `GetMeters` I would expect it to return 1 not 100 – James Ralston Nov 25 '14 at 20:09
  • 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. – Michael Nov 25 '14 at 20:13
  • 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. – Michael Nov 25 '14 at 20:14
  • 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. – James Ralston Nov 25 '14 at 20:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65640/discussion-between-james-ralston-and-michael). – James Ralston Nov 25 '14 at 20:27
  • 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. – Michael Nov 25 '14 at 20:32
  • those are equal... p = 1; c = 1; x = GetPercChange(p,c) = 0, y = p * x = 0... 100 * 0 = 0 – James Ralston Nov 25 '14 at 20:36
  • Hah. Of course I choose the corner case in my haste. Try p = 2. – Michael Nov 25 '14 at 20:42
  • this should be in chat, want to clear the air there? – James Ralston Nov 25 '14 at 20:44
  • (13,11) => -15.38 %, your result => %-15 – Hamid Pourjam Nov 26 '14 at 17:39
  • 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. – Michael Nov 26 '14 at 17:45
  • @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: http://stackoverflow.com/questions/1790975/format-decimal-for-percentage-values – Michael May 19 '15 at 22:01
  • 2
    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. – Dan Beaulieu Nov 22 '18 at 12:32
5

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;
    }
Sikandar Amla
  • 1,415
  • 18
  • 27
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");
}
Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74