Вот как бы я поступил, если бы столкнулся с этой проблемой. Сначала давайте определим очень простой класс RationalNumber, который содержит два свойства - Dividend и Divisor и оператор для добавления двух комплексных чисел. Вот как это выглядит:
public sealed class RationalNumber
{
public RationalNumber()
{
this.Divisor = 1;
}
public static RationalNumberoperator +( RationalNumberc1, RationalNumber c2 )
{
RationalNumber result = new RationalNumber();
Int64 nDividend = ( c1.Dividend * c2.Divisor ) + ( c2.Dividend * c1.Divisor );
Int64 nDivisor = c1.Divisor * c2.Divisor;
Int64 nReminder = nDividend % nDivisor;
if ( nReminder == 0 )
{
// The number is whole
result.Dividend = nDividend / nDivisor;
}
else
{
Int64 nGreatestCommonDivisor = FindGreatestCommonDivisor( nDividend, nDivisor );
if ( nGreatestCommonDivisor != 0 )
{
nDividend = nDividend / nGreatestCommonDivisor;
nDivisor = nDivisor / nGreatestCommonDivisor;
}
result.Dividend = nDividend;
result.Divisor = nDivisor;
}
return result;
}
private static Int64 FindGreatestCommonDivisor( Int64 a, Int64 b)
{
Int64 nRemainder;
while ( b != 0 )
{
nRemainder = a% b;
a = b;
b = nRemainder;
}
return a;
}
// a / b = a is devidend, b is devisor
public Int64 Dividend { get; set; }
public Int64 Divisor { get; set; }
}
Вторая часть действительно проста. Допустим, у нас есть массив чисел. Их среднее значение оценивается как сумма (числа) / длина (числа), что совпадает с числом [0] / длина + число [1] / длина + ... + число [n] / длина. Чтобы иметь возможность рассчитать это, мы представим каждое число [i] / длину как целое число и рациональную часть (напоминание). Вот как это выглядит:
Int64[] aValues = new Int64[] { long.MaxValue - 100, long.MaxValue - 200, long.MaxValue - 300 };
List<RationalNumber> list = new List<RationalNumber>();
Int64 nAverage = 0;
for ( Int32 i = 0; i < aValues.Length; ++i )
{
Int64 nReminder = aValues[ i ] % aValues.Length;
Int64 nWhole = aValues[ i ] / aValues.Length;
nAverage += nWhole;
if ( nReminder != 0 )
{
list.Add( new RationalNumber() { Dividend = nReminder, Divisor = aValues.Length } );
}
}
RationalNumber rationalTotal = new RationalNumber();
foreach ( var rational in list )
{
rationalTotal += rational;
}
nAverage = nAverage + ( rationalTotal.Dividend / rationalTotal.Divisor );
В конце у нас есть список рациональных чисел и целое число, которое мы суммируем и получаем среднее значение последовательности без переполнения. Тот же подход может быть использован для любого типа без переполнения, и нет потери точности.
EDIT:
Почему это работает:
Определить: набор чисел.
если Среднее (A) = СУММА (A) / LEN (A) =>
Среднее (A) = A [0] / LEN (A) + A [1] / LEN (A) + A [2] / LEN (A) + ..... + A [N] / LEN (2) =>
если мы определим An как число, которое удовлетворяет этому: An = X + (Y / LEN (A)), что, по сути, так, потому что если вы разделите A на B, мы получим X с напоминанием рациональное число (Y / B).
=> так
Среднее (A) = A1 + A2 + A3 + ... + AN = X1 + X2 + X3 + X4 + ... + Напоминание1 + Напоминание2 + ...;
Суммируйте целые части и суммируйте напоминания, сохраняя их в рациональной числовой форме. В конце мы получаем одно целое число и одно рациональное, которое в сумме дает Среднее (A). В зависимости от того, какую точность вы хотите, вы можете применить это только к рациональному числу в конце.