C# How to divide decimals without implicit round up
In c# when you perform the division of two decimals the last digit of the result will automatically be rounded if the true mathematical result cannot be exactly stored as a decimal type.
I want to write a function that performs the division where the last digit is always rounded down even if the digit to the right of the last would normally cause a round up.
My function would be declared as MyDivide(decimal a, decimal b)
As an example MyDivide(2.0M, 3.0M) => 0.6666666666666666666666666666
whereas the c# division operator would yie开发者_StackOverflow社区ld 2.0M / 3.0M => 0.6666666666666666666666666667
Any help implementing this is appreciated.
You're going to have to implement long division yourself. There is NO way to finagle the built-in division into doing this for you (hint: you can't distinguish between 2m / 3m
and .6666666666666666666666666667m / 1m
or 6666666666666666666666666667m / 10000000000000000000000000000m
for that matter).
There's no easy way to get it to divide the way you want, but you can detect the rounding and correct for it. Since the decimal
's mantissa is 96 bits you cannot hold it in a long
or double
, so I use a .Net 4 BigInteger
object. I just multiply the mantissas of the denominator and quotient and compare it against the numerator (adjusted for the exponent of the result of the multiplication). If the result is greater than the numerator then the division must have rounded away from zero, so I just have to subtract a 1 in the least significant position from the quotient. To do this I create a decimal with 1 for a mantissa and the quotient's exponent for its exponent.
using System;
using System.Numerics;
using System.Collections.Generic;
namespace DivTest
{
class Divide
{
public static decimal MyDivide(decimal numerator, decimal denominator)
{
var quotient = numerator / denominator;
// turn decimals into mantissas (BigInts) and exponents
int nExp, dExp, qExp;
var nMan = MantissaOfDecimal(num, out nExp);
var dMan = MantissaOfDecimal(denom, out dExp);
var qMan = MantissaOfDecimal(quotient, out qExp);
// multiply quotient times denominator and compare with numerator
if (dMan * qMan > nMan * BigInteger.Pow(10, dExp + qExp - nExp))
{
// quotient was rounded away from zero, so subtract LSB
// to round back toward zero
quotient -= new decimal(1, 0, 0, quotient < 0, (byte)qExp);
}
return quotient;
}
static BigInteger MantissaOfDecimal(decimal d, out int exponent)
{
var ints = decimal.GetBits(d);
exponent = (ints[3] >> 16) & 0xFF;
var bytes = new List<byte>(13);
// create a BigInteger containing the mantissa of the decimal value
bytes.AddRange(BitConverter.GetBytes(ints[0]));
bytes.AddRange(BitConverter.GetBytes(ints[1]));
bytes.AddRange(BitConverter.GetBytes(ints[2]));
bytes.Add(0); // avoid high bits being interpreted as negative
return new BigInteger(bytes.ToArray());
}
static void Main()
{
decimal num = 2m, denom = 3m;
Console.WriteLine("Divide: " + num / denom);
Console.WriteLine("MyDivide: " + MyDivide(num, denom));
}
}
}
The output of the above program is:
Divide: 0.6666666666666666666666666667 MyDivide: 0.6666666666666666666666666666
精彩评论