c# - truncar - sql redondear hacia arriba
¿Cómo puedo asegurarme de que una división de enteros siempre se redondea? (7)
Quiero asegurarme de que una división de enteros siempre se redondea si es necesario. ¿Hay una manera mejor que esta? Hay un montón de casting en marcha :-)
(int)Math.Ceiling((double)myInt1 / myInt2)
La respuesta final basada en int
Para enteros con signo:
int div = a / b;
if (((a ^ b) >= 0) && (a % b != 0))
div++;
Para enteros sin signo:
int div = a / b;
if (a % b != 0)
div++;
El razonamiento de esta respuesta.
La división entera '' /
'' se define para redondear hacia cero (7.7.2 de la especificación), pero queremos redondear hacia arriba. Esto significa que las respuestas negativas ya se han redondeado correctamente, pero las respuestas positivas deben ajustarse.
Las respuestas positivas que no son cero son fáciles de detectar, pero la respuesta cero es un poco más complicada, ya que puede ser el redondeo de un valor negativo o el redondeo hacia abajo de uno positivo.
La apuesta más segura es detectar cuándo la respuesta debe ser positiva al verificar que los signos de ambos enteros son idénticos. El operador xor entero '' ^
'' en los dos valores dará como resultado un bit de signo 0 cuando este sea el caso, lo que significa un resultado no negativo, por lo que la verificación (a ^ b) >= 0
determina que el resultado debería haber sido positivo antes del redondeo. También tenga en cuenta que para los enteros sin signo, cada respuesta es obviamente positiva, por lo que esta verificación se puede omitir.
La única comprobación restante es si se ha producido algún redondeo, para el cual a % b != 0
hará el trabajo.
Lecciones aprendidas
La aritmética (entera o de otro tipo) no es tan simple como parece. Pensando cuidadosamente todo el tiempo requerido.
Además, aunque mi respuesta final quizás no sea tan ''simple'' o ''obvia'' o tal vez incluso ''rápida'' como responde el punto flotante, tiene una calidad de redención muy fuerte para mí; Ahora he razonado a través de la respuesta, así que estoy realmente seguro de que es correcto (hasta que alguien más inteligente me dice lo contrario ( mirada furtiva en dirección a Eric )).
Para tener la misma sensación de certeza acerca de la respuesta del punto flotante, tendría que hacer más (y posiblemente más complicado) pensar si hay alguna condición en la cual la precisión del punto flotante pueda interferir, y si es en Math.Ceiling
quizás haga algo indeseable en las entradas ''justo lo correcto''.
El camino recorrido
Reemplazar (note que reemplacé el segundo myInt1
con myInt2
, asumiendo que era lo que quería decir):
(int)Math.Ceiling((double)myInt1 / myInt2)
con:
(myInt1 - 1 + myInt2) / myInt2
La única advertencia es que si myInt1 - 1 + myInt2
desborda el tipo de entero que está utilizando, es posible que no obtenga lo que espera.
Razón por la que esto es incorrecto : -1000000 y 3999 deben dar -250, esto da -249
EDITAR:
Teniendo en cuenta que esto tiene el mismo error que la otra solución de enteros para los valores negativos de myInt1
, podría ser más fácil hacer algo como:
int rem;
int div = Math.DivRem(myInt1, myInt2, out rem);
if (rem > 0)
div++;
Eso debería dar el resultado correcto en div
utilizando solo operaciones de enteros.
Razón por la que esto es incorrecto : -1 y -5 deben dar 1, esto da 0
EDITAR (una vez más, con sentimiento):
El operador de división redondea hacia cero; para resultados negativos, esto es exactamente correcto, por lo que solo los resultados no negativos necesitan ajustes. También teniendo en cuenta que DivRem
solo hace un /
y un %
todos modos, DivRem
la llamada (y comencemos con la comparación fácil para evitar el cálculo de módulo cuando no es necesario):
int div = myInt1 / myInt2;
if ((div >= 0) && (myInt1 % myInt2 != 0))
div++;
Razón por la que esto es incorrecto : -1 y 5 deben dar 0, esto da 1
(En mi propia defensa del último intento, nunca debí haber intentado una respuesta razonada mientras mi mente me decía que tenía 2 horas de retraso para dormir)
ACTUALIZACIÓN: Esta pregunta fue el tema de mi blog en enero de 2013 . Gracias por la gran pregunta!
Obtener aritmética de enteros correcta es difícil. Como se ha demostrado ampliamente hasta ahora, en el momento en que intentas hacer un truco "inteligente", es muy probable que hayas cometido un error. Y cuando se encuentra una falla, cambiar el código para corregir la falla sin considerar si la corrección rompe otra cosa no es una buena técnica de resolución de problemas. Hasta ahora, creo que se han publicado cinco soluciones aritméticas de enteros incorrectas diferentes para este problema completamente no especialmente difícil.
La forma correcta de abordar los problemas aritméticos de enteros, es decir, la forma en que aumenta la probabilidad de obtener la respuesta correcta la primera vez, es abordar el problema con cuidado, resolverlo paso a paso y utilizar los buenos principios de ingeniería para hacerlo. asi que.
Comience por leer la especificación de lo que está intentando reemplazar. La especificación para la división entera establece claramente:
La división redondea el resultado hacia cero.
El resultado es cero o positivo cuando los dos operandos tienen el mismo signo y cero o negativo cuando los dos operandos tienen signos opuestos
Si el operando izquierdo es el int más pequeño representable y el operando derecho es -1, se produce un desbordamiento. [...] se define por la implementación en cuanto a si [se emite una ArithmeticException] o si el desbordamiento no se reporta, y el valor resultante es el del operando de la izquierda.
Si el valor del operando derecho es cero, se lanza una excepción System.DivideByZeroException.
Lo que queremos es una función de división entera que calcula el cociente pero redondea el resultado siempre hacia arriba , no siempre hacia cero .
Así que escribe una especificación para esa función. Nuestra función int DivRoundUp(int dividend, int divisor)
debe tener un comportamiento definido para cada entrada posible. Ese comportamiento indefinido es profundamente preocupante, así que eliminémoslo. Diremos que nuestra operación tiene esta especificación:
la operación arroja si el divisor es cero
la operación se lanza si el dividendo es int.minval y el divisor es -1
Si no hay resto (la división es ''par''), el valor de retorno es el cociente integral
De lo contrario, devuelve el entero más pequeño que es mayor que el cociente, es decir, siempre se redondea.
Ahora tenemos una especificación, por lo que sabemos que podemos crear un diseño comprobable . Supongamos que agregamos un criterio de diseño adicional para que el problema se resuelva únicamente con aritmética de enteros, en lugar de calcular el cociente como un doble, ya que la solución "doble" se ha rechazado explícitamente en la declaración del problema.
Entonces, ¿qué debemos calcular? Claramente, para cumplir con nuestra especificación mientras permanecemos únicamente en aritmética de enteros, necesitamos conocer tres hechos. Primero, ¿cuál era el cociente entero? Segundo, ¿fue la división libre de resto? Y tercero, si no, ¿se calculó el cociente de enteros redondeando hacia arriba o hacia abajo?
Ahora que tenemos una especificación y un diseño, podemos comenzar a escribir código.
public static int DivRoundUp(int dividend, int divisor)
{
if (divisor == 0 ) throw ...
if (divisor == -1 && dividend == Int32.MinValue) throw ...
int roundedTowardsZeroQuotient = dividend / divisor;
bool dividedEvenly = (dividend % divisor) == 0;
if (dividedEvenly)
return roundedTowardsZeroQuotient;
// At this point we know that divisor was not zero
// (because we would have thrown) and we know that
// dividend was not zero (because there would have been no remainder)
// Therefore both are non-zero. Either they are of the same sign,
// or opposite signs. If they''re of opposite sign then we rounded
// UP towards zero so we''re done. If they''re of the same sign then
// we rounded DOWN towards zero, so we need to add one.
bool wasRoundedDown = ((divisor > 0) == (dividend > 0));
if (wasRoundedDown)
return roundedTowardsZeroQuotient + 1;
else
return roundedTowardsZeroQuotient;
}
¿Es esto inteligente? ¿No Hermosa? No. Corto? No. ¿Correcto según la especificación? Creo que sí, pero no lo he probado completamente. Aunque se ve bastante bien.
Aquí somos profesionales; Utilizar buenas prácticas de ingeniería. Investigue sus herramientas, especifique el comportamiento deseado, considere primero los casos de error y escriba el código para enfatizar su corrección obvia. Y cuando encuentre un error, considere si su algoritmo tiene fallas profundas para comenzar antes de que simplemente comience a intercambiar las direcciones de las comparaciones y rompa cosas que ya funcionan.
El problema con todas las soluciones aquí es que necesitan un reparto o tienen un problema numérico. Casting to float o double siempre es una opción, pero podemos hacerlo mejor.
Cuando usas el código de la respuesta de @jerryjvl
int div = myInt1 / myInt2;
if ((div >= 0) && (myInt1 % myInt2 != 0))
div++;
hay un error de redondeo 1/5 se redondearía, porque 1% 5! = 0. Pero esto es incorrecto, porque el redondeo solo ocurrirá si reemplaza el 1 con un 3, por lo que el resultado es 0.6. Necesitamos encontrar una manera de redondear cuando el cálculo nos dé un valor mayor o igual a 0.5. El resultado del operador de módulo en el ejemplo superior tiene un rango de 0 a myInt2-1. El redondeo solo ocurrirá si el resto es superior al 50% del divisor. Entonces el código ajustado se ve así:
int div = myInt1 / myInt2;
if (myInt1 % myInt2 >= myInt2 / 2)
div++;
Por supuesto, también tenemos un problema de redondeo en myInt2 / 2, pero este resultado le dará una mejor solución de redondeo que las otras en este sitio.
Oportunidad perfecta para utilizar un método de extensión:
public static class Int32Methods
{
public static int DivideByAndRoundUp(this int number, int divideBy)
{
return (int)Math.Ceiling((float)number / (float)divideBy);
}
}
Esto hace que tu código sea súper legible también:
int result = myInt.DivideByAndRoundUp(4);
Podrías escribir un ayudante.
static int DivideRoundUp(int p1, int p2) {
return (int)Math.Ceiling((double)p1 / p2);
}
Podrías usar algo como lo siguiente.
a / b + ((Math.Sign(a) * Math.Sign(b) > 0) && (a % b != 0)) ? 1 : 0)
Todas las respuestas aquí hasta ahora parecen demasiado complicadas.
En C # y Java, para dividendo positivo y divisor, simplemente necesita hacer:
( dividend + divisor - 1 ) / divisor