C # Как разделить десятичные дроби без неявного округления - PullRequest
6 голосов
/ 09 декабря 2010

В c # при выполнении деления двух десятичных знаков последняя цифра результата будет автоматически округлена, если истинный математический результат не может быть точно сохранен как десятичный тип.

Я хочу написать функцию, котораявыполняет деление, при котором последняя цифра всегда округляется в меньшую сторону, даже если цифра справа от последней обычно вызывает округление в большую сторону.

Моя функция будет объявлена ​​как MyDivide (десятичное число a, десятичное число b)

В качестве примера MyDivide (2,0M, 3,0M) => 0,6666666666666666666666666666

, тогда как оператор деления c # даст 2,0M / 3,0M => 0,6666666666666666666666666667

Любая помощь, реализующая этооценены.

Ответы [ 2 ]

3 голосов
/ 09 декабря 2010

Вам придется реализовать длинное деление самостоятельно.Нет никакого способа заставить встроенное разделение сделать это за вас (подсказка: вы не можете различить 2m / 3m и .6666666666666666666666666667m / 1m или 6666666666666666666666666667m / 10000000000000000000000000000m в этом отношении).

2 голосов
/ 09 декабря 2010

Нет простого способа заставить его разделить, как вы хотите, но вы можете обнаружить округление и исправить его.Поскольку мантисса decimal составляет 96 бит, вы не можете удерживать ее в long или double, поэтому я использую объект .Net 4 BigInteger.Я просто умножаю мантиссы знаменателя и частное и сравниваю их с числителем (с поправкой на показатель степени результата умножения).Если результат больше, чем числитель, то деление должно быть округлено от нуля, так что я просто должен вычесть 1 из наименее значимой позиции из частного.Для этого я создаю десятичную с 1 для мантиссы и экспоненту для ее экспоненты.

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));
        }
    }
}

Вывод вышеуказанной программы:

Divide:   0.6666666666666666666666666667
MyDivide: 0.6666666666666666666666666666
...