Uno de mis odios favoritos de los lenguajes derivados de C (como matemático) es que
(-1) % 8 // comes out as -1, and not 7
fmodf(-1,8) // fails similarly
Cual es la mejor solucion?
C ++ permite la posibilidad de sobrecargar las plantillas y el operador, pero ambos son aguas turbias para mí. ejemplos recibidos con gratitud.
%
dice que es el módulo ... es el resto .%
problema.(-1) & 8 == 7
Respuestas:
En primer lugar, me gustaría señalar que ni siquiera se puede confiar en el hecho de que
(-1) % 8 == -1
. lo único en lo que puede confiar es en eso(x / y) * y + ( x % y) == x
. Sin embargo, si el resto es negativo o no, está definido por la implementación .Ahora, ¿por qué usar plantillas aquí? Una sobrecarga de ints y longs bastaría.
int mod (int a, int b) { int ret = a % b; if(ret < 0) ret+=b; return ret; }
y ahora puedes llamarlo como mod (-1,8) y parecerá 7.
Editar: encontré un error en mi código. No funcionará si b es negativo. Entonces creo que esto es mejor:
int mod (int a, int b) { if(b < 0) //you can check for b == 0 separately and do what you want return -mod(-a, -b); int ret = a % b; if(ret < 0) ret+=b; return ret; }
Referencia: C ++ 03 párrafo 5.6 cláusula 4:
fuente
INT_MIN / -1
(en implementaciones de complemento a dos). Bajo la antigua especificación, es-32768 % -1
posible que tenga que evaluar a-65536
(que tampoco está en el rango del tipo de 16 bits, ¡puaj!) Para que la identidad se mantenga.Aquí hay una función C que maneja valores enteros positivos O negativos O fraccionarios para AMBOS OPERANDOS
#include <math.h> float mod(float a, float N) {return a - N*floor(a/N);} //return in range [0, N)
Esta es seguramente la solución más elegante desde un punto de vista matemático. Sin embargo, no estoy seguro de si es robusto en el manejo de números enteros. A veces, los errores de punto flotante se infiltran al convertir int -> fp -> int.
Estoy usando este código para no int s, y una función separada para int.
NOTA: ¡es necesario atrapar N = 0!
Código del probador:
#include <math.h> #include <stdio.h> float mod(float a, float N) { float ret = a - N * floor (a / N); printf("%f.1 mod %f.1 = %f.1 \n", a, N, ret); return ret; } int main (char* argc, char** argv) { printf ("fmodf(-10.2, 2.0) = %f.1 == FAIL! \n\n", fmodf(-10.2, 2.0)); float x; x = mod(10.2f, 2.0f); x = mod(10.2f, -2.0f); x = mod(-10.2f, 2.0f); x = mod(-10.2f, -2.0f); return 0; }
(Nota: puede compilarlo y ejecutarlo directamente desde CodePad: http://codepad.org/UOgEqAMA )
Salida:
fuente
floor()
. Además, es posible que pierda precisión cuando se convierte en flotante: pruebe(float)1000000001/3
, ¡se sorprenderá de los resultados!Acabo de notar que Bjarne Stroustrup etiqueta
%
como el operador restante , no como el operador de módulo.Apostaría a que este es su nombre formal en las especificaciones ANSI C & C ++, y que el abuso de la terminología se ha infiltrado. ¿Alguien sabe esto a ciencia cierta?
Pero si este es el caso, entonces la función fmodf () de C (y probablemente otras) son muy engañosas. deben estar etiquetados como fremf (), etc.
fuente
%
).La función general más simple para encontrar el módulo positivo sería la siguiente: funcionaría tanto en valores positivos como negativos de x.
int modulo(int x,int N){ return (x % N + N) %N; }
fuente
Para enteros, esto es simple. Solo haz
(((x < 0) ? ((x % N) + N) : x) % N)
donde estoy suponiendo que
N
es positivo y representable en el tipo dex
. Su compilador favorito debería poder optimizar esto, de modo que termine en una sola operación de modificación en ensamblador.fuente
int x=-9001; unsigned int N=2000;
da 2295, no 999.(x < 0) ? (x % N + N) : (x % N)
.La mejor solución para un matemático es usar Python.
La sobrecarga del operador C ++ tiene poco que ver con esto. No puede sobrecargar operadores para tipos integrados. Lo que quieres es simplemente una función. Por supuesto, puede usar plantillas de C ++ para implementar esa función para todos los tipos relevantes con solo 1 pieza de código.
La biblioteca C estándar proporciona
fmod
, si recuerdo el nombre correctamente, para tipos de punto flotante.Para enteros, puede definir una plantilla de función C ++ que siempre devuelva un resto no negativo (correspondiente a la división euclidiana) como ...
#include <stdlib.h> // abs template< class Integer > auto mod( Integer a, Integer b ) -> Integer { Integer const r = a%b; return (r < 0? r + abs( b ) : r); }
... y simplemente escribe en
mod(a, b)
lugar dea%b
.Aquí el tipo
Integer
debe ser un tipo entero con signo.Si desea el comportamiento matemático común donde el signo del resto es el mismo que el signo del divisor, puede hacerlo, por ejemplo,
template< class Integer > auto floor_div( Integer const a, Integer const b ) -> Integer { bool const a_is_negative = (a < 0); bool const b_is_negative = (b < 0); bool const change_sign = (a_is_negative != b_is_negative); Integer const abs_b = abs( b ); Integer const abs_a_plus = abs( a ) + (change_sign? abs_b - 1 : 0); Integer const quot = abs_a_plus / abs_b; return (change_sign? -quot : quot); } template< class Integer > auto floor_mod( Integer const a, Integer const b ) -> Integer { return a - b*floor_div( a, b ); }
… Con la misma restricción
Integer
, que es un tipo firmado.¹ Porque la división de enteros de Python se redondea hacia el infinito negativo.
fuente
r
resultado debe hacera
=r + b*(a/b)
verdadero. no importa cómo se implemente la división de enteros,b*something
es un múltiplo deb
. esto hacer
un resultado de módulo válido incluso si es negativo. puede agregarle abs (b
) y seguirá siendo un resultado de módulo válido.Oh, también odio% design para esto ...
Puede convertir dividendos a sin firmar de una manera como:
unsigned int offset = (-INT_MIN) - (-INT_MIN)%divider result = (offset + dividend) % divider
donde el desplazamiento es el más cercano a (-INT_MIN) múltiplo de módulo, por lo que sumarlo y restarlo no cambiará el módulo. Tenga en cuenta que tiene un tipo sin firmar y el resultado será un número entero. Desafortunadamente, no puede convertir correctamente los valores INT_MIN ... (- offset-1) ya que provocan un desbordamiento arifmético. Pero este método tiene la ventaja de solo una aritmética adicional por operación (y sin condicionales) cuando se trabaja con un divisor constante, por lo que se puede usar en aplicaciones similares a DSP.
Hay un caso especial, donde el divisor es 2 N (potencia entera de dos), para el cual el módulo se puede calcular usando aritmética simple y lógica bit a bit como
dividend&(divider-1)
por ejemplo
x mod 2 = x & 1 x mod 4 = x & 3 x mod 8 = x & 7 x mod 16 = x & 15
Una forma más común y menos complicada es obtener el módulo usando esta función (funciona solo con divisor positivo):
int mod(int x, int y) { int r = x%y; return r<0?r+y:r; }
Este solo resultado correcto si es negativo.
También puedes engañar:
(p% q + q)% q
Es muy corto pero usa dos% -s que normalmente son lentos.
fuente
Creo que otra solución a este problema se usaría para variables de tipo long en lugar de int.
Estaba trabajando en un código en el que el operador% devolvía un valor negativo que causaba algunos problemas (para generar variables aleatorias uniformes en [0,1] realmente no desea números negativos :)), pero después de cambiar las variables a type long, todo funcionaba sin problemas y los resultados coincidían con los que obtenía al ejecutar el mismo código en python (importante para mí, ya que quería poder generar los mismos números "aleatorios" en varias plataformas.
fuente
Aquí hay una nueva respuesta a una pregunta anterior, basada en este artículo de Microsoft Research y sus referencias.
Tenga en cuenta que desde C11 y C ++ 11 en adelante, la semántica de se
div
ha convertido en un truncamiento hacia cero (ver[expr.mul]/4
). Además, paraD
dividir pord
, C ++ 11 garantiza lo siguiente sobre el cocienteqT
y el restorT
auto const qT = D / d; auto const rT = D % d; assert(D == d * qT + rT); assert(abs(rT) < abs(d)); assert(signum(rT) == signum(D));
donde se
signum
asigna a -1, 0, +1, dependiendo de si su argumento es <, ==,> que 0 (consulte estas preguntas y respuestas para obtener el código fuente).Con división truncada, el signo del resto es igual al signo del dividendo
D
, es decir-1 % 8 == -1
. C ++ 11 también proporciona unastd::div
función que devuelve una estructura con miembrosquot
yrem
acuerdo con la división truncada.Hay otras definiciones posibles, por ejemplo, la denominada división en suelo se puede definir en términos de la división truncada incorporada.
auto const I = signum(rT) == -signum(d) ? 1 : 0; auto const qF = qT - I; auto const rF = rT + I * d; assert(D == d * qF + rF); assert(abs(rF) < abs(d)); assert(signum(rF) == signum(d));
Con división en suelo, el signo del resto es igual al signo del divisor.
d
. En lenguajes como Haskell y Oberon, hay operadores integrados para la división por suelo. En C ++, necesitaría escribir una función usando las definiciones anteriores.Otra forma más es la división euclidiana , que también se puede definir en términos de la división truncada incorporada.
auto const I = rT >= 0 ? 0 : (d > 0 ? 1 : -1); auto const qE = qT - I; auto const rE = rT + I * d; assert(D == d * qE + rE); assert(abs(rE) < abs(d)); assert(signum(rE) != -1);
Con la división euclidiana, el signo del resto es siempre positivo .
fuente
Para una solución que no usa ramas y solo 1 mod, puede hacer lo siguiente
// Works for other sizes too, // assuming you change 63 to the appropriate value int64_t mod(int64_t x, int64_t div) { return (x % div) + (((x >> 63) ^ (div >> 63)) & div); }
fuente
... o simplemente acostumbrarse a obtener cualquier representante para la clase de equivalencia.
fuente
r
. El%
operador no tiene nada que ver con las clases de equivalencia. Es el operador del resto y el resto está bien definido algebraicamente como no negativo y menor que el divisor. Lamentablemente, C lo definió de manera incorrecta. Aún así, +1 por tener una de las mejores respuestas.Plantilla de ejemplo para C ++
template< class T > T mod( T a, T b ) { T const r = a%b; return ((r!=0)&&((r^b)<0) ? r + b : r); }
Con esta plantilla, el resto devuelto será cero o tendrá el mismo signo que el divisor (denominador) (el equivalente al redondeo hacia el infinito negativo), en lugar de que el comportamiento de C ++ del resto sea cero o tenga el mismo signo que el dividendo ( numerador) (el equivalente a redondear hacia cero).
fuente
define MOD(a, b) ((((a)%(b))+(b))%(b))
fuente
unsigned mod(int a, unsigned b) { return (a >= 0 ? a % b : b - (-a) % b); }
fuente
Esta solución (para usar cuando
mod
es positiva) evita tomar operaciones negativas de división o resto todas juntas:int core_modulus(int val, int mod) { if(val>=0) return val % mod; else return val + mod * ((mod - val - 1)/mod); }
fuente
Yo lo haría:
((-1)+8) % 8
Esto agrega el último número al primero antes de hacer el módulo dando 7 como se desee. Esto debería funcionar para cualquier número hasta -8. Para -9 agregue 2 * 8.
fuente
-99999
?