Arithmetic operators
Los operadores aritméticos aplican operaciones matemáticas estándar a sus operandos.
|
Esta sección está incompleta
Razón: considerar un ToC de propósito más general para esta y otras tablas que cubren múltiples temas |
| Operador | Nombre del operador | Ejemplo | Resultado |
|---|---|---|---|
| + | unario más | + a | el valor de a después de las promociones |
| - | unario menos | - a | el negativo de a |
| + | suma | a + b | la suma de a y b |
| - | resta | a - b | la resta de b de a |
| * | producto | a * b | el producto de a y b |
| / | división | a / b | la división de a entre b |
| % | resto | a % b | el resto de a dividido entre b |
| ~ | bitwise NOT | ~a | el bitwise NOT de a |
| & | bitwise AND | a & b | el bitwise AND de a y b |
| | | bitwise OR | a | b | el bitwise OR de a y b |
| ^ | bitwise XOR | a ^ b | el bitwise XOR de a y b |
| << | bitwise left shift | a << b | a desplazado a la izquierda por b |
| >> | bitwise right shift | a >> b | a desplazado a la derecha por b |
Contenidos |
Desbordamientos
La aritmética de enteros sin signo siempre se realiza
módulo 2
n
donde n es el número de bits en ese entero particular. Por ejemplo, para
unsigned
int
, sumar uno a
UINT_MAX
da como resultado
0
, y restar uno de
0
da como resultado
UINT_MAX
.
Cuando una operación aritmética de enteros con signo desborda (el resultado no cabe en el tipo de resultado), el comportamiento es indefinido: puede envolver según las reglas de la representación (normalmente complemento a 2), puede generar una trampa en algunas plataformas o debido a opciones del compilador (por ejemplo
-ftrapv
en GCC y Clang), o puede ser completamente
optimizado por el compilador
.
Entorno de punto flotante
Si
#pragma STDC FENV_ACCESS
está establecido en
ON
, todos los operadores aritméticos de punto flotante obedecen la
dirección de redondeo
actual de punto flotante y reportan errores aritméticos de punto flotante como se especifica en
math_errhandling
a menos que formen parte de un
inicializador estático
(en cuyo caso no se generan excepciones de punto flotante y el modo de redondeo es al más cercano)
Contracción de punto flotante
A menos que
#pragma STDC FP_CONTRACT
esté configurado en
OFF
, toda aritmética de punto flotante puede realizarse como si los resultados intermedios tuvieran rango y precisión infinitos, es decir, optimizaciones que omiten errores de redondeo y excepciones de punto flotante que se observarían si la expresión se evaluara exactamente como está escrita. Por ejemplo, permite la implementación de
(
x
*
y
)
+
z
con una única instrucción de CPU de multiplicación-fusión-suma o la optimización de
a
=
x
*
x
*
x
*
x
;
como
tmp
=
x
*
x
;
a
=
tmp
*
tmp
.
Sin relación con la contratación, los resultados intermedios de la aritmética de punto flotante pueden tener un rango y precisión que difieren de los indicados por su tipo, consulte FLT_EVAL_METHOD
Aritmética unaria
Las expresiones de operadores aritméticos unarios tienen la forma
+
expresión
|
(1) | ||||||||
-
expresión
|
(2) | ||||||||
| expression | - | expresión de cualquier arithmetic type |
Tanto el unario más como el unario menos aplican primero integral promotions a su operando, y luego
- unary plus devuelve el valor después de la promoción
- unary minus devuelve el negativo del valor después de la promoción (excepto que el negativo de un NaN es otro NaN)
El tipo de la expresión es el tipo después de la promoción, y la categoría de valor es no lvalue.
Notas
El menos unario provoca comportamiento indefinido debido al desbordamiento de enteros con signo cuando se aplica a INT_MIN , LONG_MIN , o LLONG_MIN , en plataformas típicas (complemento a 2).
En C++, el operador unario
+
también puede utilizarse con otros tipos incorporados como arrays y funciones, no así en C.
#include <stdio.h> #include <complex.h> #include <limits.h> int main(void) { char c = 'a'; printf("sizeof char: %zu sizeof int: %zu\n", sizeof c, sizeof +c); printf("-1, where 1 is signed: %d\n", -1); // Comportamiento definido ya que la aritmética se realiza para entero sin signo. // Por lo tanto, el cálculo es (-1) módulo (2 elevado a n) = UINT_MAX, donde n es // el número de bits de unsigned int. Si unsigned int es de 32 bits, entonces esto // da (-1) módulo (2 elevado a 32) = 4294967295 printf("-1, where 1 is unsigned: %u\n", -1u); // Comportamiento indefinido porque el valor matemático de -INT_MIN = INT_MAX + 1 // (es decir, 1 más que el valor máximo posible para signed int) // // printf("%d\n", -INT_MIN); // Comportamiento indefinido porque el valor matemático de -LONG_MIN = LONG_MAX + 1 // (es decir, 1 más que el valor máximo posible para signed long) // // printf("%ld\n", -LONG_MIN); // Comportamiento indefinido porque el valor matemático de -LLONG_MIN = LLONG_MAX + 1 // (es decir, 1 más que el valor máximo posible para signed long long) // // printf("%lld\n", -LLONG_MIN); double complex z = 1 + 2*I; printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z)); }
Salida posible:
sizeof char: 1 sizeof int: 4 -1, where 1 is signed: -1 -1, where 1 is unsigned: 4294967295 -(1+2i) = -1.0-2.0
Operadores aditivos
Las expresiones del operador aritmético aditivo binario tienen la forma
lhs
+
rhs
|
(1) | ||||||||
lhs
-
rhs
|
(2) | ||||||||
-
- ambos tienen arithmetic types , incluyendo complejos e imaginarios
- uno es un puntero a tipo de objeto completo, el otro tiene tipo entero
-
- ambos tienen tipos aritméticos , incluyendo complejos e imaginarios
- lhs tiene puntero a tipo de objeto completo, rhs tiene tipo entero
- ambos son punteros a objetos completos de tipos compatibles , ignorando calificadores
Suma y resta aritméticas
Si ambos operandos tienen tipos aritméticos , entonces
- primero, usual arithmetic conversions se realizan
- luego, los valores de los operandos después de las conversiones se suman o restan siguiendo las reglas usuales de las matemáticas (para la resta, rhs se resta de lhs ), excepto que
-
- si un operando es NaN, el resultado es NaN
- infinito menos infinito es NaN y FE_INVALID se activa
- infinito más infinito negativo es NaN y FE_INVALID se activa
La suma y resta de números complejos e imaginarios se definen de la siguiente manera (nótese que el tipo del resultado es imaginario si ambos operandos son imaginarios y complejo si un operando es real y el otro imaginario, según lo especificado por las conversiones aritméticas habituales):
| + o - | u | iv | u + iv |
|---|---|---|---|
| x | x ± u | x ± iv | (x ± u) ± iv |
| iy | ±u + iy | i(y ± v) | ±u + i(y ± v) |
| x + iy | (x ± u) + iy | x + i(y ± v) | (x ± u) + i(y ± v) |
// work in progress // note: take part of the c/language/conversion example
Aritmética de punteros
-
Si el puntero
Papunta a un elemento de un arreglo con índiceI, entonces
-
-
P
+
N
y
N
+
P
son punteros que apuntan a un elemento del mismo array con índice
I+N -
P
-
N
es un puntero que apunta a un elemento del mismo array con índice
I-N
-
P
+
N
y
N
+
P
son punteros que apuntan a un elemento del mismo array con índice
El comportamiento está definido solo si tanto el puntero original como el puntero resultante apuntan a elementos del mismo array o una posición más allá del final de ese array. Nótese que ejecutar p-1 cuando p apunta al primer elemento de un array es comportamiento indefinido y puede fallar en algunas plataformas.
-
Si el puntero
P1apunta a un elemento de un array con índiceI(o una posición después del final) yP2apunta a un elemento del mismo array con índiceJ(o una posición después del final), entonces
-
- P1 - P2 tiene el valor igual a I - J y el tipo ptrdiff_t (que es un tipo entero con signo, típicamente la mitad del tamaño del objeto más grande que se puede declarar)
El comportamiento está definido solo si el resultado cabe en ptrdiff_t .
Para el propósito de la aritmética de punteros, un puntero a un objeto que no es un elemento de ningún array se trata como un puntero al primer elemento de un array de tamaño 1.
// work in progress int n = 4, m = 3; int a[n][m]; // VLA of 4 VLAs of 3 ints each int (*p)[m] = a; // p == &a[0] p = p + 1; // p == &a[1] (pointer arithmetic works with VLAs just the same) (*p)[2] = 99; // changes a[1][2]
Operadores multiplicativos
Las expresiones de operadores aritméticos multiplicativos binarios tienen la forma
lhs
*
rhs
|
(1) | ||||||||
lhs
/
rhs
|
(2) | ||||||||
lhs
%
rhs
|
(3) | ||||||||
- primero, usual arithmetic conversions son realizadas. Luego...
Multiplicación
El operador binario * realiza la multiplicación de sus operandos (tras las conversiones aritméticas habituales) siguiendo las definiciones aritméticas usuales, excepto que
- si un operando es un NaN, el resultado es un NaN
- la multiplicación de infinito por cero da NaN y FE_INVALID se genera
- la multiplicación de infinito por un valor distinto de cero da infinito (incluso para argumentos complejos)
Debido a que en C, cualquier valor complejo con al menos una parte infinita es un infinito incluso si su otra parte es un NaN, las reglas aritméticas habituales no se aplican a la multiplicación complejo-complejo. Otras combinaciones de operandos flotantes siguen la siguiente tabla:
| * | u | iv | u + iv |
|---|---|---|---|
| x | xu | i(xv) | (xu) + i(xv) |
| iy | i(yu) | −yv | (−yv) + i(yu) |
| x + iy | (xu) + i(yu) | (−yv) + i(xv) | reglas especiales |
Además del manejo de infinitos, la multiplicación de números complejos no debe permitir el desbordamiento de resultados intermedios, excepto cuando
#pragma STDC CX_LIMITED_RANGE
está configurado en
ON
, en cuyo caso el valor puede calcularse como si fuera mediante
(x+iy)×(u+iv) = (xu-yv)+i(yu+xv)
, ya que el programador asume la responsabilidad de limitar el rango de los operandos y manejar los infinitos.
A pesar de no permitir desbordamiento indebido, la multiplicación de números complejos puede generar excepciones de punto flotante espurias (de lo contrario, es prohibitivamente difícil implementar versiones que no desborden)
#include <stdio.h> #include <stdio.h> #include <complex.h> #include <math.h> int main(void) { // TODO casos más simples, tomar algunos de C++ double complex z = (1 + 0*I) * (INFINITY + I*INFINITY); // la fórmula del libro de texto daría // (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN // pero C da un infinito complejo printf("%f + i*%f\n", creal(z), cimag(z)); // la fórmula del libro de texto daría // cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN // pero C da ±∞+i*nan double complex y = cexp(INFINITY + I*NAN); printf("%f + i*%f\n", creal(y), cimag(y)); }
Salida posible:
inf + i*inf inf + i*nan
División
El operador binario
/
divide el primer operando por el segundo (tras las conversiones aritméticas habituales) siguiendo las definiciones aritméticas usuales, excepto que
- cuando el tipo después de las conversiones aritméticas usuales es un tipo entero, el resultado es el cociente algebraico (no una fracción), redondeado en dirección definida por la implementación (hasta C99) truncado hacia cero (desde C99)
- si un operando es un NaN, el resultado es un NaN
-
si el primer operando es un infinito complejo y el segundo operando es finito, entonces el resultado del operador
/es un infinito complejo -
si el primer operando es finito y el segundo operando es un infinito complejo, entonces el resultado del operador
/es un cero.
Debido a que en C, cualquier valor complejo con al menos una parte infinita se considera un infinito incluso si su otra parte es un NaN, las reglas aritméticas habituales no se aplican a la división complejo-complejo. Otras combinaciones de operandos flotantes siguen la siguiente tabla:
| / | u | iv |
|---|---|---|
| x | x/u | i(−x/v) |
| iy | i(y/u) | y/v |
| x + iy | (x/u) + i(y/u) | (y/v) + i(−x/v) |
Además del manejo de infinitos, la división compleja no debe desbordar resultados intermedios, excepto cuando
#pragma STDC CX_LIMITED_RANGE
está configurado a
ON
, en cuyo caso el valor puede calcularse como si fuera mediante
(x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u
2
+v
2
)
, ya que el programador asume la responsabilidad de limitar el rango de los operandos y manejar los infinitos.
A pesar de prohibir el desbordamiento indebido, la división compleja puede generar excepciones de punto flotante espurias (de lo contrario, es prohibitivamente difícil implementar versiones que no se desborden)
Si el segundo operando es cero, el comportamiento es indefinido, excepto que si la aritmética de punto flotante IEEE está soportada, y se está realizando la división de punto flotante, entonces
- Dividir un número distinto de cero por ±0.0 da como resultado el infinito con el signo correcto y FE_DIVBYZERO se activa
- Dividir 0.0 por 0.0 da como resultado NaN y FE_INVALID se activa
Resto
El operador binario % produce el resto de la división del primer operando por el segundo (después de las conversiones aritméticas habituales).
El signo del resto se define de tal manera que si el cociente
a/b
es representable en el tipo de resultado, entonces
(
a
/
b
)
*
b
+
a
%
b
==
a
.
Si el segundo operando es cero, el comportamiento es indefinido.
Si el cociente
a/b
no es representable en el tipo de resultado, el comportamiento tanto de
a/b
como de
a%b
es indefinido (esto significa que
INT_MIN
%-
1
es indefinido en sistemas de complemento a 2)
Nota: el operador de resto no funciona con tipos de punto flotante, la función de biblioteca fmod proporciona esa funcionalidad.
Lógica bit a bit
Las expresiones de operadores aritméticos bit a bit tienen la forma
~
rhs
|
(1) | ||||||||
lhs
&
rhs
|
(2) | ||||||||
lhs
|
rhs
|
(3) | ||||||||
lhs
^
rhs
|
(4) | ||||||||
donde
| lhs , rhs | - | expresiones de tipo entero |
Primero, los operadores & , ^ , y | realizan conversiones aritméticas usuales en ambos operandos y el operador ~ realiza promociones enteras en su único operando.
Luego, los operadores lógicos binarios correspondientes se aplican bit a bit; es decir, cada bit del resultado se establece o se borra según la operación lógica (NOT, AND, OR o XOR), aplicada a los bits correspondientes de los operandos.
Nota: los operadores bit a bit se utilizan comúnmente para manipular conjuntos de bits y máscaras de bits.
Nota: para tipos sin signo (después de la promoción), la expresión ~E es equivalente al valor máximo representable por el tipo de resultado menos el valor original de E .
Salida posible:
Promoted mask: 0x000000f0 Value: 0x12345678 Setting bits: 0x123456f8 Clearing bits: 0x12345608 Selecting bits: 0x00000070
Operadores de desplazamiento
Las expresiones del operador de desplazamiento bit a bit tienen la forma
lhs
<<
rhs
|
(1) | ||||||||
lhs
>>
rhs
|
(2) | ||||||||
donde
| lhs , rhs | - | expresiones de tipo entero |
Primero, las promociones enteras se realizan, individualmente, en cada operando (Nota: esto es diferente a otros operadores aritméticos binarios, que realizan conversiones aritméticas usuales). El tipo del resultado es el tipo de lhs después de la promoción.
El comportamiento es indefinido si rhs es negativo o es mayor o igual al número de bits en el lhs promovido.
Para un
lhs
sin signo, el valor de
LHS << RHS
es el valor de
LHS * 2
RHS
, reducido módulo el valor máximo del tipo de retorno más 1 (es decir, se realiza un desplazamiento a la izquierda bit a bit y los bits que se desplazan fuera del tipo de destino se descartan). Para un
lhs
con signo con valores no negativos, el valor de
LHS << RHS
es
LHS * 2
RHS
si es representable en el tipo promovido de
lhs
, de lo contrario el comportamiento es indefinido.
Para
lhs
sin signo y para
lhs
con signo con valores no negativos, el valor de
LHS >> RHS
es la parte entera de
LHS / 2
RHS
. Para
LHS
negativo, el valor de
LHS >> RHS
está definido por la implementación, donde en la mayoría de las implementaciones esto realiza un desplazamiento aritmético a la derecha (de modo que el resultado permanece negativo). Por lo tanto, en la mayoría de las implementaciones, desplazar a la derecha un
LHS
con signo llena los nuevos bits de orden superior con el bit de signo original (es decir, con 0 si no era negativo y con 1 si era negativo).
#include <stdio.h> enum {ONE=1, TWO=2}; int main(void) { char c = 0x10; unsigned long long ulong_num = 0x123; printf("0x123 << 1 = %#llx\n" "0x123 << 63 = %#llx\n" // overflow truncates high bits for unsigned numbers "0x10 << 10 = %#x\n", // char is promoted to int ulong_num << 1, ulong_num << 63, c << 10); long long long_num = -1000; printf("-1000 >> 1 = %lld\n", long_num >> ONE); // implementation defined }
Salida posible:
0x123 << 1 = 0x246 0x123 << 63 = 0x8000000000000000 0x10 << 10 = 0x4000 -1000 >> 1 = -500
Referencias
- Estándar C17 (ISO/IEC 9899:2018):
-
- 6.5.3.3 Operadores aritméticos unarios (p: 64)
-
- 6.5.5 Operadores multiplicativos (p: 66)
-
- 6.5.6 Operadores aditivos (p: 66-68)
-
- 6.5.7 Operadores de desplazamiento de bits (p: 68)
-
- 6.5.10 Operador AND bit a bit (p: 70)
-
- 6.5.11 Operador OR exclusivo bit a bit (p: 70)
-
- 6.5.12 Operador OR inclusivo bit a bit (p: 70-71)
- Estándar C11 (ISO/IEC 9899:2011):
-
- 6.5.3.3 Operadores aritméticos unarios (p: 89)
-
- 6.5.5 Operadores multiplicativos (p: 92)
-
- 6.5.6 Operadores aditivos (p: 92-94)
-
- 6.5.7 Operadores de desplazamiento bit a bit (p: 94-95)
-
- 6.5.10 Operador AND bit a bit (p: 97)
-
- 6.5.11 Operador OR exclusivo bit a bit (p: 98)
-
- 6.5.12 Operador OR inclusivo bit a bit (p: 98)
- Estándar C99 (ISO/IEC 9899:1999):
-
- 6.5.3.3 Operadores aritméticos unarios (p: 79)
-
- 6.5.5 Operadores multiplicativos (p: 82)
-
- 6.5.6 Operadores aditivos (p: 82-84)
-
- 6.5.7 Operadores de desplazamiento de bits (p: 84-85)
-
- 6.5.10 Operador AND bit a bit (p: 87)
-
- 6.5.11 Operador OR exclusivo bit a bit (p: 88)
-
- 6.5.12 Operador OR inclusivo bit a bit (p: 88)
- Estándar C89/C90 (ISO/IEC 9899:1990):
-
- 3.3.3.3 Operadores aritméticos unarios
-
- 3.3.5 Operadores multiplicativos
-
- 3.3.6 Operadores aditivos
-
- 3.3.7 Operadores de desplazamiento de bits
-
- 3.3.10 Operador AND bit a bit
-
- 3.3.11 Operador OR exclusivo bit a bit
-
- 3.3.12 Operador OR inclusivo bit a bit
Véase también
| Operadores comunes | ||||||
|---|---|---|---|---|---|---|
| asignación |
incremento
decremento |
aritméticos | lógicos | comparación |
acceso a
miembros |
otros |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
b
]
|
a
(
...
)
|
|
Documentación de C++
para
Operadores aritméticos
|