Итого по математике> 100% - PullRequest
       7

Итого по математике> 100%

8 голосов
/ 10 февраля 2012

У меня есть 3 значения, которые округляются до десятичных разрядов (т. Е. 5%, 25%, 70%), за исключением того, что мои 70% на самом деле составляют 70,6%, поэтому получается 71%, что составляет мои 5%, 25%, 71% = 101%, что нехорошо, особенно потому, что у меня есть гистограмма, которая, если она превышает 100% ширины, становится действительно испорченной.

Как я могу убедиться, что мои 3 значения никогда не превышают 100%? сейчас я использую Math.Round (значение, 0). Есть ли какие-либо переключатели / опции, которые я могу использовать, чтобы мои 3 значения не превышали 100%?

Спасибо

Ответы [ 8 ]

11 голосов
/ 10 февраля 2012

Если три значения всегда должны суммироваться до 100, тогда округлите только два значения и вычислите третье с помощью

third = 100 - Math.Round(a) - Math.Round(b);

Примечание. Ваш результат также может быть слишком маленьким. Если у вас есть три значения 33,333333, округление и сложение их даст 99!


РЕДАКТИРОВАТЬ (в ответ на комментарий @ BlueRajaDannyPflughoeft)

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

double[] values = new double[] { 17.2, 3.7, 4.6, 5.8 };
int[] percent = new int[values.Length];

double sum = values.Sum();
int totalPercent = 100;
for (int i = 0; i < values.Length; i++) {
    double rawPercent = sum == 0 ? 0 : values[i] / sum * totalPercent;
    sum -= values[i];
    int roundedPercent = (int)Math.Round(rawPercent);
    totalPercent -= roundedPercent;
    percent[i] = roundedPercent;
}
// values         =  { 17.2,   3.7,    4.6,    5.8 }
//
// Percents:
// percent        => { 55,     12,     15,     18 }
// raw (exact)    => { 54.952, 11.821, 14.696, 18.530 }   (rounded to 3 decimals)
// raw continuous => { 54.952, 11.809, 14.596, 18.000 }

Это не идеально, однако ошибка никогда не должна превышать 1%. Вот еще один пример

values         =  { 10.0,   10.0,   10.0,   10.0,   10.0,   10.0 }

Percents:
rounded        => { 17,     17,     16,     17,     16,     17 }
raw (exact)    => { 16.667, 16.667, 16.667, 16.667, 16.667, 16.667 }   
raw continuous => { 16.667, 16.600, 16.500, 16.667, 16.500  17.000 }
5 голосов
/ 10 февраля 2012

Не добавляйте округленные значения и ожидайте получить что-нибудь значимое. Добавьте необоснованные значения. Если это не дает того, что вы хотите, вы должны объяснить, что, по вашему мнению, вы делаете, добавив округленные значения.

2 голосов
/ 10 февраля 2012

Если вам все равно, что значения могут составить меньше 100, простое решение - просто использовать Math.Floor.

Становится интереснее, если вы хотите округлить вверх / вниз и получить результат ровно 100. Вот один из подходов - ndash; округлите их все вниз, затем выборочно округлите их сначала по наибольшей доле, пока снова не наберете 100:

public static void RoundTo100Percent(IList<double> list)
{
    // get the sort order before changing any items
    IList<int> sortOrder = list.Select((n, i) => new { Key = n - Math.Truncate(n), Index = i }).OrderByDescending(ki => ki.Key).Select(ki => ki.Index).ToList();

    // round them all down to start with
    for (int i = 0; i < list.Count; i++)
    {
        list[i] = Math.Floor(list[i]);
    }

    // then round them up selectively, starting with those with the highest fractional part
    // e.g. 5.9 will round up to 6.0 before 7.8 rounds up to 8.0.
    int numberOfItemsToRoundUp = 100 - (int)list.Sum();
    foreach (int sortIndex in sortOrder.Take(numberOfItemsToRoundUp))
    {
        list[sortIndex]++;
    }
}
1 голос
/ 10 февраля 2012

Ну, если вы не округлите, то все они составят 100%.Используйте поплавки / двойные / десятичные числа.Проблема решена.

В конце концов, зачем вам уменьшать вашу точность?

1 голос
/ 10 февраля 2012

Не могли бы вы просто сделать проверку в конце, как это:

if (total > 100)
  total = 100;
0 голосов
/ 14 сентября 2012

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

Важно понимать, что тамнет «правильного» способа сделать это.Например, если заданы три значения, каждое из которых равно 1/3, а округленная сумма равна 99, к какому значению следует добавить 1, чтобы общее значение было равно 100?Там нет правильного ответа.Просто выберите один.

Вот мое решение в PHP:

function ensureColumnAddsToOneHundred(&$rows, $columnIndex)
{
   $percentagesTotalError = getColumnTotal($rows, $columnIndex) - 100;

   if ($percentagesTotalError > count($rows))
   {
      throw new Exception('Percentages total rounding error larger than nValues!');
   }

   // Add or subtract one from as many rows as is
   // necessary to ensure that the total is exactly 100.
   for ($i = 0; $i < abs($percentagesTotalError); ++$i)
   {
      $rows[$i][$columnIndex] += ($percentagesTotalError > 0)? -1: 1;
   }

   if (getColumnTotal($rows, $columnIndex) != 100)
   {
      throw new Exception('Percentages total not equal to 100 even after corrections!');
   }
}
0 голосов
/ 10 февраля 2012

Почему бы не сложить все 3 значения вместе, , а затем округлить окончательный результат?33,3 + 33,3 + 33,3 = 99,9, которые затем округляются до 100, при округлении по отдельности получается только 99, как при каждом округлении вниз.

0 голосов
/ 10 февраля 2012

Вероятно, лучше округлить все счета, а затем оставить остаток в виде "статистического округления" или чего-то подобного.

...