2012-02-09 9 views
8

Tengo 3 valores que se redondean a ningún decimal (es decir, 5%, 25%, 70%) excepto que mi 70% es realmente 70,6%, lo que equivale al 71% lo que hace que Total 5%, 25%, 71% = 101%, lo cual no es bueno, especialmente porque tengo un gráfico de barras que si supera el 100% de ancho, se pone realmente mal.Emisión de redondeo matemático total> 100%

¿Cómo puedo asegurarme de que mis 3 valores nunca superen más del 100%? ahora estoy usando Math.Round (valor, 0) ¿hay algún interruptor/opción que pueda usar para mantener mi total de mis 3 valores por encima del 100%?

Gracias

+8

Post. Tu. Código. – Oded

+0

Bueno, ¿por qué no escribes la suma en una variable y compruebas que no supera el límite especificado? Alternativamente, podría forzar los valores a redondearse al entero más grande más pequeño que el valor real ... – Abrixas2

Respuesta

1

¿No puedes hacer una verificación al final de esta manera:

if (total > 100) 
    total = 100; 
+3

pero entonces, ¿cuál de los 3 valores necesita reducir? y por cuánto? –

+0

Ese es un problema diferente. ¿Estamos seguros de que eso es lo que OP necesita? Él nunca lo solicita explícitamente. – Oleksi

+4

O vaya con 'Math.Min (total, 100)' en su lugar. –

11

Si los tres valores siempre tienen que resumir a continuación 100 ronda sólo dos de los valores y calcular el tercero con

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

Nota: Su resultado también podría ser demasiado pequeño. Si tiene tres valores siendo 33.333333, ¡al redondearlos y agregarlos obtendrá 99!


EDITAR (en respuesta al comentario de @ BlueRajaDannyPflughoeft)

Si usted tiene muchos valores, la suma de todos los errores de redondeo podría llegar a ser grande. Por lo tanto, no sería una buena idea cambiarlo al último valor. Para estos casos, sugiero un redondeo continuo.

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 } 

No es perfecto, sin embargo, el error nunca debe exceder el 1%. Aquí hay otro ejemplo

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 } 
+0

Buena idea, pero esto supone que el total siempre será del 100%. Lo cual no está claro a partir de la pregunta. –

+2

Todavía estoy por no redondear en primer lugar. Todavía tendrías problemas con 33.33333, pero eso es inevitable sin importar el método que uses, a menos que los fuerces a sumar hasta el 100%. – Zenexer

+1

Esta solución no funciona bien, especialmente con más de tres valores. Con, por ejemplo, 10 valores, puede tener el valor final redondeando hacia arriba o hacia abajo hasta un 5% de distancia de su valor real. –

0

Es probablemente el mejor para redondear hacia abajo en todas las cuentas y luego tener el saldo restante como "redondeo estadístico" o algo similar.

5

No agregue valores redondeados y espere obtener algo significativo. Agregue los valores sin redondear. Si eso no hace lo que quieres, tienes que explicar lo que crees que estás logrando agregando valores redondeados.

1

Bueno, si no redondeas, todas ellas sumarán hasta el 100%. Use flotantes/dobles/decimales. Problema resuelto.

Después de todo, ¿por qué quieres disminuir tu precisión? - que es lo que hace el redondeo, por cierto.

2

Si no le importa que los valores puedan sumar menos de 100, la solución simple es simplemente usar Math.Floor en todo.

Se vuelve más interesante si quiere redondear hacia arriba/abajo y el resultado es exactamente 100. Aquí hay un enfoque – redondeando hacia abajo, luego redondee hacia arriba selectivamente la fracción más grande primero hasta que vuelva a 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]++; 
    } 
} 
0

por qué no añadir los 3 valores juntos, entonces redondear el resultado final? 33.3 + 33.3 + 33.3 = 99.9 que luego se redondea a 100, el redondeo individual solo saldría en 99 ya que cada uno redondearía hacia abajo.

0

Este problema surgió cuando tuve que importar datos en un sistema que requería porcentajes enteros para el conjunto de filas pertenecientes a cada usuario para agregar exactamente a cien.

Es importante darse cuenta de que no hay una forma "correcta" de hacerlo. Por ejemplo, dados tres valores cada uno igual a 1/3, teniendo un total redondeado igual a 99, ¿a qué valor se debería agregar 1 para forzar el total a 100? No hay respuesta correcta. Solo elige uno.

Aquí está mi solución en 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!'); 
    } 
}