Performing Math operations on decimal datatype in C#?

23,775

Solution 1

In most cases involving a decimal (currency etc), it isn't useful to take a root; and the root won't have anything like the expected precision that you might expect a decimal to have. You can of course force it by casting (assuming we aren't dealing with extreme ends of the decimal range):

decimal root = (decimal)Math.Sqrt((double)myVariableHere);

which forces you to at least acknowledge the inherent rounding issues.

Solution 2

I don't understand why all the answers to that question are the same.

There are several ways to calculate the square root from a number. One of them was proposed by Isaac Newton. I'll only write one of the simplest implementations of this method. I use it to improve the accuracy of double's square root.

// x - a number, from which we need to calculate the square root
// epsilon - an accuracy of calculation of the root from our number.
// The result of the calculations will differ from an actual value
// of the root on less than epslion.
public static decimal Sqrt(decimal x, decimal epsilon = 0.0M)
{
    if (x < 0) throw new OverflowException("Cannot calculate square root from a negative number");

    decimal current = (decimal)Math.Sqrt((double)x), previous;
    do
    {
        previous = current;
        if (previous == 0.0M) return 0;
        current = (previous + x / previous) / 2;
    }
    while (Math.Abs(previous - current) > epsilon);
    return current;
}

About speed: in the worst case (epsilon = 0 and number is decimal.MaxValue) the loop repeats less than a three times.

If you want to know more, read this (Hacker's Delight by Henry S. Warren, Jr.)

Solution 3

I just came across this question, and I'd suggest a different algorithm than the one SLenik proposed. This is based on the Babylonian Method.

public static decimal Sqrt(decimal x, decimal? guess = null)
{
    var ourGuess = guess.GetValueOrDefault(x / 2m);
    var result = x / ourGuess;
    var average = (ourGuess + result) / 2m;

    if (average == ourGuess) // This checks for the maximum precision possible with a decimal.
        return average;
    else
        return Sqrt(x, average);
}

It doesn't require using the existing Sqrt function, and thus avoids converting to double and back, with the accompanying loss of precision.

Share:
23,775
Qcom
Author by

Qcom

Updated on June 04, 2020

Comments

  • Qcom
    Qcom almost 4 years

    I was wondering if the above was at all possible. For example:

    Math.Sqrt(myVariableHere);
    

    When looking at the overload, it requires a double parameter, so I'm not sure if there is another way to replicate this with decimal datatypes.

  • farfareast
    farfareast over 11 years
    Bobson, you proposed another possible solution, but there is no flaw in SLenik's solution because he only uses double Sqrt(double) as a starting point - like a seed. Then, in the loop, he uses the full precision decimal x to adjust the resulting value of Sqrt.
  • Bobson
    Bobson over 11 years
    @farfareast - It's a valid point, and under most circumstances the precision issue won't matter. but if you have a very precise decimal in the first place, you will lose a portion of it, which could lead to a slightly-off result. I also find it philosophically objectionable to calculate the square root by using the square root, although that certainly wouldn't stop me from coding that if I actually needed it and there was a performance reason to do so.
  • Bobson
    Bobson over 11 years
    Also, in my initial testing (and post), my algorithm appeared to be something like 100x faster, but upon further testing, I discovered that the order in which I did the tests affected the results, so I removed that part. It's still a valid algorithm, and I think it's slightly faster, but it's only a minor difference. The built-in double version of Sqrt is still significantly faster than either.
  • farfareast
    farfareast over 11 years
    No wonder double sqrt is faster. Look at this quote from this wikipedia article: Floating point instructions x86 assembly language includes instructions for a stack-based floating point unit. They include addition, subtraction, negation, multiplication, division, remainder, square roots, integer truncation, fraction truncation, and scale by power of two. In particular there is FSQRT instruction in the Intel machine code instruction set. :-) See also: here
  • ave
    ave over 9 years
    Well, I'm making an app calculates golden ratio, which will eventually go to decimal range. Won't work in my scenario.
  • Sachin Kainth
    Sachin Kainth over 5 years
    I think this is a really awesome solution. Thanks.