Datos, Operadores y Expresiones

Sitio: Facultad de Ingeniería U.Na.M.
Curse: Computación ET-344
Libro: Datos, Operadores y Expresiones
Impreso por: Cuenta visitante
Fecha: sábado, 26 de abril de 2025, 20:47

1. Datos

Como se había mencionado antes, “Cada dato utilizado durante la ejecución del programa deberá ser DECLARADO para que el compilador lo reconozca, sepa cómo almacenarlo y recuperarlo; se deberá reservar memoria para que pueda almacenar la cantidad de datos necesarios.”

C++ NO tiene tipado Dinámico como Python.

La declaración de cada ítem de datos consiste en indicar

  1. tipo de dato
  2. nombre que lo identifique.

En C hay tipos básicos de datos:

  • carácter (character)
  • entero (integer)
  • punto flotante (floating point) 
  • puntero (point)
  • lógico (bool)
  • varios mas...

Los números son los datos fundamentales utilizados por los programas de computadoras. El contenido de la memoria consiste en números binarios almacenados en grupos de 8 bits (1 byte) o 16 bits (2 bytes). Aún cuando un programa de computadora trabaje con letras o gráficos, básicamente está involucrando una serie de números almacenados en memoria.

Los diferentes tipos de datos dependen, en realidad, de la cantidad de bytes que ocupen en memoria.

Cuando se desea definir ítems de datos que almacenen palabras (dos o más caracteres) nos encontramos ante el tipo de dato “string” o cadena de caracteres.
Por ejemplo:

'hola'  'Juan Pérez'

Este tipo de datos merece un desarrollo especial por ello se verá en el capítulo de arreglos.

Tipo de datos char NO es lo mismo que string.

2. Variables

El ítem de dato llamado variable, se refiere a un espacio de memoria cuyo contenido se modificará de acuerdo a las circunstancias durante la ejecución del programa.

Cada variable utilizada en el programa deberá ser declarada antes de ser utilizada.
La declaración de una variable le indica al lenguaje que se pretende utilizar una variable particular y qué tipo de dato almacenará. La declaración se forma por un tipo de dato seguido de uno o más nombres de variables.

La inicialización de una variable significa asignarle al espacio de memoria reservado un valor particular.

Resulta conveniente realizarlo porque cuando se declara una variable, el espacio de memoria reservado, podrá contener cualquier valor.

Una variable DECLARADA pero NO INICIALIZADA, no ocupa lugar en la memoria.

  • int z;   // Variable Declarada como entera.
  • float i=3.21; //Variable Declarada como float e inicializada a 3.21

La inicialización NO siempre es necesaria, por ejemplo:

#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
    float x; // declaro x como variable float
    cout<<"ingrese un valor";
    cin>>x; // ingreso por teclado un valor que es almacenado en x
    cout<<x+1; //sumo 1 a x y muestro por pantalla
    return 0;
}

En el caso anterior vemos que NO es necesario inicializar la variable en línea 5, ya que luego ( línea 6) se le "carga" un valor... esto sin duda debe hacerce antes de usar la variable x. (línea 7)

#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
    float x; // declaro x como variable float
    cout<<"ingrese un valor";
    //cin>>x; // ingreso por teclado un valor que es almacenado en x
    cout<<x+1; //sumo 1 a x y muestro por pantalla
    return 0;
}

Este código tiraría una ADVERTENCIA que no es lo mismo que un  error:

Donde claramente vemos que nos avisa, nos advierte que estamos usando una variable que NO fue inicializada (uninitialized)

2.1. Identificadores

Un identificador es el nombre que permite identificar un espacio de memoria (o nombres de varaibles) mediante un nombre válido para el lenguaje.
El lenguaje C++ es flexible pero posee ciertas reglas que debemos respetar cuando definimos identificadores para las variables que utilizaremos en el programa.


Reglas para dar nombre a las variables:

  • Sólo se pueden usar letras (mayúsculas o minúsculas), números y ciertos caracteres no alfanuméricos, como el '_', pero nunca un punto, coma, guión, comillas o símbolos matemáticos o interrogaciones.
  • El primer carácter no puede ser un número.
  • C y C++ distinguen entre mayúsculas y minúsculas, de modo que los identificadores número y Número son diferentes.
  • Los primeros 32 caracteres son significativos, esto significa que La_cantidad_total_de_dinero_en_mi_cuenta y La_cantidad_total_de_dinero_en_mi_banco serán consideradas como la misma variable.

2.2. Declaración de variables

Una característica del C++ es la necesidad de la declaración de las variables que se usarán en el programa. Aunque esto resulta chocante para los que se aproximan al C++ desde otros lenguajes de programación, es en realidad una característica muy importante y útil de C++, ya que ayuda a conseguir códigos más compactos y eficaces, y contribuye a facilitar la depuración, la detección y corrección de errores.

Sintaxis:
     [tipo] [lista_de_identificadores];

Tipo debe ser un tipo de datos válido y lista_de_identificadores puede ser uno o más identificadores separados por coma (,). La declaración de variables puede considerarse como una sentencia. Desde este punto de vista, la declaración terminará con un ";".
Por ejemplo:
     int numero;
     float promedio, final;
     char letra;

También es posible inicializar las variables dentro de la misma declaración.
Por ejemplo:
     int a = 1234;
     bool seguir = true, encontrado;
     char letra = ‘k’;

Se declarara las variables "a", "seguir", "encontrado" y “letra”; y además se  inicia los valores de "a" en 1234, “seguir” en "true" y “letra” con ‘k’.

Mas ejemplos de declaraciones :



En C++, contrariamente a lo que sucede con otros lenguajes de programación, las variables no inicializadas tienen un valor indeterminado, contienen lo que normalmente se denomina "basura", también en esto hay excepciones como veremos más adelante.

Declaración de una variable de tipo entero (integer)

En C++ las posibilidades de declara varaibles enteras son MUCHAS mas que en Python, recordemos que cuando hablamos de esto no estamos refiriendo TIPO  indirectamente al TAMAÑO que usan en memoria las variables. Veamos algunos.


El ejemplo anterior es solo una parte de los posibles tipos de variables enteras en C++, en realidad la combinación de varias palabras claves nos darían el resto de tipos de variables, como ser:

[signed|unsigned] [short|long|long long] int <identificador o nombre >
[signed|unsigned] long long [int] <identificador o nombre>
[signed|unsigned] long [int] <identificador o nombre >
[signed|unsigned] short [int] <identificador o nombre >

Declaración de una variable de tipo punto flotante (float)

float <identificador o nombre >

Declaración de una variable de tipo punto flotante de doble precisión (double)

[long] double <identificador o nombre >

Veamos otro ejemplo, en este vamos a usar el operador sizeof() que me da el tamaño del argumento en bytes.

}



Declaración de una variable sin tipo (void)

void <identificador o nombre >

Este es una variable especial que indica la ausencia de tipo. Se usa en funciones que no devuelven ningún valor, también en funciones que no requieren parámetros, aunque este uso sólo es obligatorio en C, y opcional en C++, también se usará en la declaración de punteros genéricos. Es algo veremos mas adelante, por el momento profundizaremos en este tipo de variable.

Declaración de una variable de tipo enumerado (enum)

Una enumeración es un tipo de dato distinto, es un tipo de dato definido por el usuario o programador, que contiene un conjunto de nombres llamados "enumeradores " de allí el nombre de enum, los cuales son constantes enteras. Veamos un ejemplo de como sería la declaración

enum [<nombre_o_identificador_de_enum>] { <nombre> [= <valor>], ...} [lista_de_variables];

La idea de este tipo de datos es agrupar constantes simbólicas para dar mas claridad al programa. Una constante simbólica en un nombre que luego al compilar se reemplaza por un valor, pero al momento de codificar el código fuente es mas legible y evitar que aparezcan en el código números mágicos.

Ejemplo de Constante Simbólica.

#define temperatuta_ambiente 25

Por ejemplo de declaración de enum.

Para este caso enero=0, febrero=1, etc. el compilador asigna valores.
Si definimos de la siguiente manera:

El compilador dará a agosto el valor de 8 y 9 para septiembre.

 Los enumeradores o constantes simbólicos DEBEN ser únicos no se pueden repetir, y si no le damos valor el compilador les da valores.Los valores de las constantes SI se pueden REPETIR.

Observación: Si hubiera dos constantes simbólicas en DISTINTAS variables enum, el compilador tiraría ERROR.

El error sería:

Veamos otro ejemplo:

Se deja al alumno observar y razonar cada línea del código para entender como la variable mes que se declara como int se le asigna agosto..

Declaración de una variable de tipo boleana (boolean)

bool <identificador o nombre> 

Las variables de este tipo sólo pueden tomar dos valores "true" o "false". Sirven para evaluar expresiones lógicas.
Este tipo de variables se puede usar para almacenar respuestas, por ejemplo: ¿Posees carné de conducir? O para almacenar informaciones que sólo pueden tomar dos valores, por ejemplo: qué mano usas para escribir. En estos casos debemos acuñar una regla, en este ejemplo, podría ser diestro->"true", zurdo->"false".

Veamos un ejemplo:

Vemos que en la línea 7 la expresión que evalúa el if es el valor de la variable booleana SI que como vale true, se evalúa como verdadero, esto sería lo mismo que escribir:

para ambos casos la salida sería.

2.3. Inicialización de variables

En C se pueden asignar valores a la mayoría de las variables a la vez que se las declara.
La inicialización de una variable se realiza poniendo un signo igual y una constante después del identificador.

Sintaxis:

    tipo identificador = constante;

Por ejemplo:

    char a = ‘p’; int num = 0;

3. Punto Fijo

Comercialmente existen numerosos dispositivos digitales, tales como microprocesadores (uP), microcontroladores (uC), procesadores y controladores digitales de señal (DSP y DSC), etc., que son  capaces de efectuar operaciones aritméticas complejas. Muchas de estas operaciones requieren de la utilización de números reales. Debido a que los dispositivos mencionados sólo operan con números binarios, y en este sistema no pueden representarse todos los números reales, los resultados obtenidos sólo son aproximaciones. Para las operaciones aritméticas, los números reales pueden expresarse de forma aproximada a través de la representación en los formatos de “Punto Flotante” y “Punto Fijo.

Veremos cada uno de ellos y las operaciones posibles.


3.1. Little Endian vs Big Endian

Cuando hablamos de Little Endian y Big Endian, nos referimos al orden en que los bytes se almacenan en la memoria los ceros y unos en una computadora para representar valores numéricos.

Los datos en la memoria se almacenan como secuencias de bytes. Sin embargo, el orden en el que estos bytes se organizan puede variar según la arquitectura del sistema:

  • Little Endian → El byte menos significativo (LSB, Least Significant Byte) se almacena primero (en la dirección de memoria más baja).
  • Big Endian → El byte más significativo (MSB, Most Significant Byte) se almacena primero (en la dirección de memoria más baja).
Supongamos que queremos almacenar el número 0x12345678 (este entero se almacena en 4 bytes) en memoria.

Su almacenamiento como Big Endian sería:
Dirección |  Valor
-------------------
  1.   0x1000  |  12
  2.   0x1001  |  34
  3.   0x1002  |  56
  4.   0x1003  |  78
Aquí, el byte más significativo (0x12) se guarda en la dirección más baja (0x1000).

Su almacenamiento como Little Endian sería:
Dirección |  Valor
-------------------
  1.   0x1000  |  78
  2.   0x1001  |  56
  3.   0x1002  |  34
  4.   0x1003  |  12
Aquí, el byte menos significativo (0x78) se guarda en la dirección más baja (0x1000).



¿Dónde se Usa Cada Uno?
  • Big Endian: Se usa en arquitecturas como Redes (TCP/IP) y algunos procesadores como los PowerPC y antiguas arquitecturas Motorola.

  • Little Endian: Es el formato usado por Intel x86 y la mayoría de las arquitecturas modernas.


Conclusión:
Si vamos a trabajar y realizar operaciones binarias, tomando bit a bit DEBEMOS saber el orden en que se guardan en memoria para poder hacer las opeaciones de manera correcta!!.

  • Little Endian: Guarda el byte menos significativo primero.
  • Big Endian: Guarda el byte más significativo primero.
Códido en C++ para determinar si el equipo e Little Endian o Big Endian.

Veamos un código fuente para determinar si el harward es Little o Big Endian.

3.2. Decimal a binario.

Recordatorio: Partes de la división.



Un ejemplo de número binario con punto fijo, sería el que resulte de convertir un número real con decimales a binario.

1234/100 = 12.34     ó -12345/100= -123.45

Los números enteros, pueden representarse de diferentes formas, tales como: Signo-Magnitud, (12.34 ó -123.34) o como Complemento a 1 y Complemento a 2, estas dos representaciónes son  de especial importancia, ya que es la utilizada en las operaciones aritméticas, epecialmente Complemento a 2.


Representación Binaria de Números enteros.

Vimos que para convertir un número entero decimal a binario, se divide por 2 hasta que el resto sea menor a 2 ( o sea 1 ó 0) y  luego se tomaban el último cociente y  "juntamos" desde el final al inicio:

Esto en C++ sería:


La salida de este script es:


Como vimos en Informática, esto proviene de la expresión:


Que permite  convertir desde cualquier sistema numérico de base "r"  a la Decimal.

Aqui dejamos otra versión mas compacta. Las líneas comentadas de los cout, permiten ver como se va formando el número binario.



4. Operaciones Binarias.

Operaciones Aritméticas

Para efectuar operaciones aritméticas, hay que tener en cuenta que las mismas deben arrojar resultados que se encuentren dentro del conjunto de números naturales.

Podríamos preguntarnos porque?

1. La representación binaria en la memoria es finita. Los números en una computadora están representados en una cantidad fija de bits (por ejemplo, 8, 16, 32 o 64 bits). Esto impone restricciones en los valores que se pueden almacenar y calcular.

2. Evitar resultados fuera del conjunto esperado (Números Naturales).Si estamos realizando operaciones en el conjunto de los números naturales (ℕ = {0, 1, 2, 3, ...}), debemos asegurarnos de que:

  • El resultado no sea negativo (porque los números naturales no incluyen valores negativos).
  • El resultado no exceda el límite máximo de la representación (por ejemplo, en un sistema de 8 bits, el valor máximo sin signo es 255).

3. Errores en resta o sustracción: resultados negativos inesperados.Por ejemplo, no podrá efectuarse una resta donde el minuendo es menor al sustraendo (ya que daría un número negativo, no representable en los números naturales), tampoco se podría realizar una división cuyo resultado arroje decimales, el resultado sólo se puede expresar mediante números naturales.

Existen otros como errores en Complemento a dos, tema que veremos mas adelante.

4.1. Suma

La suma de dos números binarios naturales se efectúa bit a bit, desde el bit menos significativo hacia el más significativo, generándose el bit de acarreo siempre que es necesario.  

Para efectuar la suma debe tenerse en cuenta que: 

  • 0+0=0
  • 0+1=1 
  • 1+0=1 
  • 1+1=10 (2 en binario)
  •  1+1+1=11 (3 en binario)
Veamos un par de ejemplos:

Observaciónes: 

  • El bit de acarreo (CARRY BIT) está presente en dispositivos digitales que realizan operaciones aritméticas ( se puede leer!) , sirve para indicar que el resultado ha sobrepasado el rango de representación (en este caso de 0 a 15).
  • Se puede ver la importancia de saber si el sistema es Little endian o Big Endian!

4.2. Resta

 Esta operación se efectúa restando bit a bit los números comenzando desde el bit menos significativo hacia el más significativo. 

Para efectuar la resta debe tenerse en cuenta que:

  • 0–0=0
  • 1–0=1 
  • 1–1=0  
  • 0–1=1 ya que debe pedirse prestado “1” al siguiente bit de más peso para efectuar 10­–1=1.

Para comprende esto, veamos el siguiente ejemplo, donde realizamos la resta de números naturales A–B, siendo A2=11000 y B2=00011.


Como puede verse en el ejemplo, cuando no puede efectuarse la resta se “pide prestado” al bit más significativo siguiente. Debe recordarse que la resta de números naturales, puede realizarse siempre que el minuendo es mayor o igual al sustraendo.

4.3. División


Esta operación puede realizarse restando sucesivamente el divisor del dividendo.

Las restas son realizadas hasta obtener “0” o no poder efectuar más la operación. 

Por cada resta realizada se incrementa en uno el cociente. 

En el siguiente ejemplo es indicado el método. Debe recordarse que en el conjunto de números naturales no podrán resolverse de forma exacta aquellas divisiones donde el dividendo no es múltiplo del divisor.

Realizar las siguientes divisiones entre números naturales:

A) A2=1001÷ B2=0010

En este  caso no puede obtenerse el resultado exacto ya que el dividendo no es múltiplo del divisor

B) A2=11000 ÷ B2=00100


Puede observarse que al efectuar la división por la unidad seguida de ceros, el resultado corresponde al dividendo desplazado hacia la derecha tantos bits como ceros posee el divisor ( zona gris) .

En Comunicaciones Digitales se verá con mas detalle el desplazamiento o corrimiento de un número.


4.4. Multiplicación

Esta operación puede efectuarse como en el sistema decimal o repitiendo el multiplicando desplazado hacia la izquierda, conforme a la posición que ocupen los unos del multiplicador. 

0Luego deben sumarse todos los valores desplazados. A este método se denomina de suma sucesiva.

Veamos el siguiente ejemplo.

Realizar las siguientes multiplicaciones entre números naturales:

A) A2=0011× B2=0101



B) A2=1110 × B2=0011.

C) A2=0011 × B2=1000.


Si el dispositivo digital que efectúa las multiplicaciones del ejemplo, opera con palabras de 4 bits, no podría representar el resultado de la segunda multiplicación, ya que el mismo excede el rango de los números con los 4 bits (de 0 a 15)Faltaría Justificar el porque?

En el tercer caso del ejemplo, puede observarse al resultado como el multiplicando desplazado hacia la izquierda en tres posiciones. El desplazamiento de un número hacia la izquierda se produce cada vez que es efectuada la multiplicación del mismo por la unidad seguida de ceros (como en el sistema decimal). En el sistema binario la unidad seguida de ceros es representada por el equivalente binario a 2n, siendo “n” el número de ceros (n=1;2;3;…).

Veamos un caso de interes para Comunicaciones 2.

En Comunicaciones 2, se utiliza un mecanismo para detectar errores, el mismo utiliza en el cálculo una multiplicación y cociente de polinomios. Vamos a ver el tema de Multiplicación.

Supongamos que tenemos un número binario A : 1101112 y lo multiplicamos por B: 10002, vamos a ver que sucede:

Conclusión:
Vemos a el multiplicar A x B que estamos desplazando A !!!

En Comunicaciones 2, se usa esto para "agregar" en los ceros que resultan del desplazamiento una cadena binaria, de esta manera el mensaje ( valor A 1101112 en este caso) no se "mezcla" con el valor que ser agregaría que llamamos R al dezplazar , ejemplo R= 1012 , quedando:
 __A__  _R_
110111   101


5. Representación binaria de decimales

El conjunto de los números enteros está conformado por los números naturales y sus opuestos (incluyendo el “0”), es decir son números con signo (…;-2;-1;0;+1;+2;…). Para representarlos en el sistema binario, el bit más significativo de la palabra es utilizado como bit de signo. De esta forma la representación de los números enteros en el sistema binario, mediante palabras de N bits, queda:




La forma indicada posee dos variantes para la representación de los números enteros: “Signo-Magnitud” y “Complemento a 2”, siendo esta última la utilizada por los dispositivos digitales que poseen capacidad para efectuar operaciones aritméticas.

Veamos como se implementa en la práctica.

5.1. Complemento a dos

La forma indicada posee dos variantes para la representación de los números enteros: “Signo-Magnitud” y “Complemento a 2”, siendo esta última la utilizada por los dispositivos digitales que poseen capacidad para efectuar operaciones aritméticas.

En la representación binaria de “Signo-Magnitud”, el bit de signo permite representar a números positivos y negativos de acuerdo a lo siguiente:

  • S = 0; número entero positivo.
  • S = 1; número entero negativo.

La magnitud (o valor absoluto) del número es representada directamente en binario puro.

Ejemplo A2=10111  B2=01100.

  • Para A2 :  1 0111  =  – 7
  • Para B2 :  0 1100  =  + 12

Complemento a dos

Otra forma de representar a los números binarios enteros es en “complemento a 2”. En esta representación, los números positivos son expresados como si estuvieran en “Signo-Magnitud”, mientras que los números negativos se cambia a complemento a 2.

Como es el complemento a dos?

El complemento a 2 de un número binario se obtiene en dos pasos:

  1. invirtiendo bit a bit el número (a esto se denomina complemento a 1) 
  2.  sumándole “1”

Nota: cabe aclarar que en la representación de complemento a 2 el MSB (Bit Mas Significatido) siempre es el bit de signo y su representación es igual que en “Signo-Magnitud”. 

El siguiente ejemplo muestra cómo obtener el complemento a 2.

Ejemplo 1 :Representar Complemento a dos A2=10110

En este ejemplo el bit de signo de A es 1, por lo tanto es un número negativo. Si complementamos a 2 obtenemos (como se muestra en la imagen)  obtenemos la magnitud de A, el complemento a 2 de este número es 01010  que sería el 1010 , por lo tanto A2=10110 es -1010.

Ejemplo 2 :Representar Complemento a dos B2=01100



En este caso el bit de signo de B es 0, por lo tanto es un número positivo. En este caso, se tiene B10= +12 (011002) y su opuesto será B2= 101002(–12).

Podemos ver que de manera sencilla el complemento a dos permite cambiar de una representación binaria negativa a positiva.


Por que se suma 1 en el complemento a 2?

Vimos que la receta para calcular el complemento a 2 es:
  1. invirtiendo bit a bit el número (a esto se denomina complemento a 1) 
  2.  sumándole “1”. 
Queda preguntarse por que simplemente no usamos la regla 1 ( complemento a 1) en el cálculo?. Veamos que pasaría.

Si solo cambiamos, invertimos el bit a bit (complemento a 1) el cero quedaría: 11111 ó 00000

Esto provoca que haya dos representaciones del cero:

  • 00000000 = +0

  • 11111111 = -0 (problema ❌)

Veamos un ejemplo del problema con 4 bits.
  • +3  =  0011
  • -3  =  1100  (Complemento a 1)
  • +0  =  0000
  • -0  =  1111  ❌ (problema)
Tener -0 (1111)  y 0 (0000),  causa ambigüedades y requiere lógica adicional en el hardware para tratar ambos valores como iguales.

Con el complemento a 2, se soluciona el problema del doble cero cambiando la forma de representar los números negativos, se invierten los bits (como en complemento a 1), se suma 1 al resultado para obtener el negativo.

Beneficios del uso de Complemento a dos.

1. La suma y resta se realizan de la misma manera que en números sin signo.
2. No hay dos representaciones del cero.
3. El bit más significativo actúa como un indicador de signo.
4. La resta se convierte en suma.
5. Simplicidad en comparación de números.Se pueden comparar números directamente con las operaciones lógicas de la CPU.Como los números negativos en complemento a 2 son mayores que los positivos en términos de orden binario, las comparaciones (>, <) funcionan sin necesidad de ajustes adicionales.
6. Manejo natural del desbordamiento. Es fácil detectar desbordamiento (overflow).
En complemento a 2, si dos números del mismo signo se suman y el resultado cambia de signo inesperadamente, ha ocurrido un desbordamiento.

Conclusión: ¿Por qué Complemento a 2 es el estándar?

Suma y resta funcionan igual que en binario normal.
No hay dos representaciones para el cero.
El bit más significativo indica el signo directamente.
La resta se convierte en suma, reduciendo la complejidad del hardware.
El desbordamiento es fácil de detectar.
Las comparaciones funcionan sin lógica extra.



hasta Página 8 del apunte de Guillermo

5.2. Expresión Matemática

Para convertir al sistema decimal, un número binario entero representado en complemento a 2, puede utilizarse la siguiente expresión para N bits:

x10= - bN-1 x 2N-1 +bN-2 x 2N-2 + bN-3 x 2N-3 +..... b1 x 21 +b0    (1)

Recordemos como se debe interpretar o crear la expresión a partir de un número binario para expresarlo en forma de polinomio.

Supongamos que tenemos el número binario A=101001012 , en nuesto caso cantidad de bits es 8,  N=8

La expresión (1) quedaría para N-1=7:

x10= -  17x 27 + 06 x 26 +15 x 25 +04 x 24 + 03 x 23 +12 x 22 + 01 x 21 + 10 x 20

x10= -  1x 27 + 0 +15 x 25 +0 + 0 +1 x 22 + + 1 x 20

x10= -  1x 128 + 0 +1 x 32 +0 + 0 +1 x 4 + + 1 x 1

x10= - 128 +32 + 4 + 1  => x10=9110 (=101001012 )


6. Punto Flotante

Comercialmente existen, dispositivos digitales (como ser procesadores digitales de señal, etc.) que trabajan con operaciones aritméticas utilizando números binarios representados en coma o punto flotante. En esta representación, el punto o coma decimal no se halla en una posición fija dentro de una secuencia de N bits, sino que se indica como una potencia de la base. El beneficio que esto trae es un aumento en el rango de números que podrán expresarse mediante una palabra con una cantidad determinada de bits. La representación en punto flotante está basada en la denominada “notación científica”. 

Ejemplo:

  • 4520000000000 = 4,52. 1012 = 452. 10 10
  • 0,00000000000452 = 4,52. 10 – 12
En la representación a través de notación científica existen cuatro campos o componentes: Signo (S), Mantisa (M), Base (b) y Exponente (E). Por lo tanto, cualquier número puede representarse como:
 

N = S M (b)E    (1)

  1. N = Número representado
  2. S = Signo del número 
  3. M = Mantisa (valor absoluto)
  4. b = Base del sistema de numeración utilizado (10 = Sist. Decimal; 2 = Sist. Binario) y
  5. E = Exponente.
Los dispositivos digitales almacenan un número N(1) en formato de punto flotante en dos partes:

  1. signo con la mantisa  ( S(2) y M(3))
  2. otra al exponente. E(5)

La base no necesita representarse ya que está implícita (al trabajar con el sistema binario, se supone que la b=2(4) ). La mantisa y el exponente son números sin signo.

Como se ha visto en el ejemplo anterior, la coma o punto decimal puede ocupar cualquier lugar, pudiéndose representar un mismo número de distintas formas.

Ejemplo:

Representemos 6.25 10 en formato binario flotante.


Paso 1: Convertir la parte entera a binario

Primero, convertimos la parte entera del número, que es 6, a binario.

  • Dividimos sucesivamente entre 2 y tomamos los restos:

    • 6÷2=3 → resto 0

    • 3÷2=1 → resto 1

    • 1÷2=0 → resto 1

  • Leemos los restos de abajo hacia arriba: 110.

Entonces, 610=1102.


Paso 2: Convertir la parte fraccionaria a binario

Ahora, convertimos la parte fraccionaria, que es 0.25, a binario.

  • Multiplicamos sucesivamente por 2 y tomamos la parte entera:

    • 0.25×2=0.5 → parte entera 0

    • 0.5×2=1.0 → parte entera 1

  • Leemos las partes enteras de arriba hacia abajo: 01.

Entonces, 0.2510=0.012.


Paso 3: Combinar parte entera y fraccionaria

Combinamos la parte entera y la fraccionaria en binario:

6.2510=110.012

Paso 4: Normalizar el número binario

Para representar el número en punto flotante, lo normalizamos en la forma 1.mantisa×2exponente.

  • Movemos el punto decimal para que quede un solo dígito antes del punto:

    110.012=1.10012×22
  • Aquí:

    • Mantisa: 1001 (los dígitos después del punto).

    • Exponente: 2 (porque movimos el punto dos lugares a la izquierda).


Paso 5: Aplicar el estándar IEEE 754

El estándar IEEE 754 define cómo representar números en punto flotante. Para un número de 32 bits (precisión simple):

  1. Bit de signo: 1 bit (0 para positivo, 1 para negativo).

  2. Exponente: 8 bits (se usa un sesgo de 127).

  3. Mantisa: 23 bits.

5.1. Bit de signo

Como 6.25 es positivo, el bit de signo es:

Signo=0
5.2. Exponente

El exponente en la normalización es 2. Para convertirlo al formato IEEE 754, le sumamos el sesgo de 127:

Exponente=2+127=129

Ahora, convertimos 129 a binario:

12910=100000012
5.3. Mantisa

La mantisa es 1001. En IEEE 754, se usan 23 bits para la mantisa, por lo que completamos con ceros a la derecha:

Mantisa=10010000000000000000000

Paso 6: Combinar todo

Finalmente, combinamos el bit de signo, el exponente y la mantisa:

Signo=0Exponente=10000001Mantisa=10010000000000000000000

La representación completa en 32 bits es:

01000000110010000000000000000000

Resumen

El número 6.25 en decimal se representa en binario de punto flotante (precisión simple) como:

  • Bit de signo: 0

  • Exponente: 10000001

  • Mantisa: 10010000000000000000000


7. Constantes

En C++ se pueden definir constantes de dos formas, ambas válidas para nosotros. La primera es por medio del comando #define nombre_constante valor y la segunda es usando la palabra clave const, veamos ahora cada una de estas formas en detalle.

La instrucción #define nos permite declarar constantes (y algunas cosas más) de una manera rápida y sencilla. Hay que tener en cuenta que al declarar constantes con #define debemos hacerlo despues de los #include para importar librerías pero antes de declarar nuestras funciones y demás. NO VA EL TIPO DE DATO.

La instrucción const nos permite declarar constantes de una manera más adecuada y acorde. Las constantes declaradas con const poseen un tipo de dato asociado (como debería ser siempre) y se declaran al interior de nuestro código como un tipo cualquiera. VA EL TIPO DE DATO.

Las constantes son muy similares a las variables, con la diferencia que éstas solo pueden tomar un valor en el momento de la declaración, luego cualquier intento de modificación será tomado como un error por parte del compilador. Las constantes NO cambian de valor en un programa, por eso son constante.
Por ejemplo:

La salida sería :

Se pide al alumnos observar y razonar sobre las distintas formas de declarar constantes en el código anterior.

Constantes "long"

Para trabajar con valores constantes "long" debemos usar el sufijo "L". Esto resulta conveniente, sobre todo, al utilizar las constantes en expresiones condicionales y, por coherencia, también en expresiones de asignación.

Esta sentencia hará que el compilador emita un error ya que no puede usar un tamaño mayor sin una indicación explícita.
Hay casos en los que los tipos "long" e "int" tienen el mismo tamaño, en ese caso no se producirá error, pero no podemos predecir que nuestro programa se compilará en un tipo concreto de compilador o plataforma.

Constantes "long long"

Para trabajar con valores constantes "long long" debemos usar el  prefijo long long , sobre todo cuando esas constantes aparecen en expresiones condicionales o de asignación.

Constantes "unsigned"

Del mismo modo, cuando trabajamos con valores constantes "unsigned" debemos usar el prefijo unsigned en la declacración" para las mismas situaciones que hemos indicado 

Constantes en punto flotante

En este último caso, cuando la constante de punto flotante se pueda confundir con un entero, debemos añadir el ".0".Para expresar constantes en punto flotante también podemos usar notación exponencial, por ejemplo:


El formato exponencial consiste en un número, llamado mantisa, que puede ser entero o con decimales, seguido de una letra 'e' o 'E' y por último, otro número, en este caso un número entero, que es el exponente de una potencia de base 10.

Los valores anteriores equivalen a:

  • x = 10 x 104 = 100000
  • y = 4,12 x 102 = 412
  • pi = 3.141592 x 100 = 3.141592

La salida sería:

Constantes "double"

Constantes "long double"


Constantes "char"

Las constantes de tipo "char" se representan entre comillas sencillas o simples , por ejemplo 'a', '8', 'F'.

Al igual que las variables, las constantes dependiendo del tipo van a usar mas o menos espacio en memoria.

La salida de este código sería:

8. Operadores

Los operadores son elementos que disparan ciertos cálculos cuando son aplicados a variables o a otros objetos en una expresión.
Un operador es un símbolo que le dice al compilador que realice manipulaciones matemáticas o lógicas específicas.
El lenguaje C++ tiene las siguientes clases de operadores: aritméticos, relacionales, lógicos y sobre bits.
Hay varios tipos de operadores, clasificados según el tipo de objetos sobre los que actúan.

Los tipos de operadores que veremos son:

  • Operadores Aritméticos.
  • Operadores Relacionales.
  • Operadores Lógicos.
  • Operadores de Asignación.
  • Operador sizeof.
  • Operador condicional.
  • Operador coma.

8.1. Operadores aritméticos

Los operadores aritméticos se utilizan para crear expresiones matemáticas.

Operadores aritméticos unitarios

Los operadores aritméticos unitarios que utiliza C++ son: ++', '--'

Sintaxis:

    <variable> ++ /* post-incremento */
  ++ <variable> /* pre-incremento */
 <variable>-- /* post-decremento */
 -- <variable> /* pre-decremento */
Operadores '++' y '--'

Los otros dos operadores unitarios '++' y '--' son un tanto especiales, ya que sólo pueden trabajar sobre variables, pues implican una asignación.
El primero ('++') incrementa el valor del operando y el segundo ('--') lo decrementa, ambos en una unidad.

Existen dos modalidades, dependiendo de que se use el operador en la forma de prefijo o de sufijo.
En su forma de prefijo, el operador es aplicado antes de que se evalúe el resto de la expresión; en la forma de sufijo, se aplica después de que se evalúe el resto de la expresión.

Por ejemplo:
en las siguientes expresiones "a" vale 100 y "b" vale 10:

    c = a + ++b;

En este primer ejemplo primero se aplica el pre-incremento, y b valdrá 11 a continuación se evalúa la expresión "a+b", que dará como resultado 111, y por último se asignará este valor a c, que valdrá 111.

Por ejemplo:

     c = a + b++;

En este segundo ejemplo primero se avalúa la expresión "a+b", que dará como resultado 110, y se asignará este valor a c, que valdrá 110.
Finalmente se aplica en post-incremento, y b valdrá 11.
Los operadores unitarios sufijos (post-incremento y post-decremento) se evalúan después de que se han evaluado el resto de las expresiones.
En el primer ejemplo primero se evalúa ++b, después a+b y finalmente c=<resultado>.
En el segundo ejemplo, primero se evalúa a+b, después c = <resultado> y finalmente b++.

Es muy importante no pensar o resolver las expresiones C++ como ecuaciones matemáticas, NO SON EXPRESIONES MATEMÁTICAS.

No veas estas expresiones como ecuaciones, NO SON ECUACIONES.

La salida de correr este código sería:

Se pide al alumno analizar la salida para comprender el pre y pos incremento/decremento.

Operadores aritméticos binarios.

Los operadores binarios que utiliza el lenguaje C++ son: ' +', ' - ',  ' * ', ' / ', ‘ % '

Sintaxis:

    <expresión>  +  <expresión> /*Sintaxis de operador suma */
<expresión>  -  <expresión> /* Sintaxis de operador resta */
<expresión>  *  <expresión> /* Sintaxis de operador multiplicación */
<expresión>  /  <expresión> /* Sintaxis de operador división */
<expresión>  %  <expresión> /* Sintaxis de operador resto */

Evidentemente se trata de las conocidas operaciones aritméticas de suma, resta, multiplicación y división y los operadores se comportan como en cualquier lenguaje de computadoras.

Debemos tener en cuenta, por ejemplo, que el operador división ( / ) aplicado a un entero truncará (perderá) cualquier resto.

Por ejemplo:

  

La salida de este código será 3.

Se pide al alumno analizar y ver si puede anticipar las salidas de los siguientes códigos:

A) B) C) D)

Operador Módulo:

El operador módulo '%', devuelve el resto de la división entera del primer operando entre el segundo. Por esta razón no puede ser aplicado a operando en coma flotante (ya que al ser números de punto flotante no hay resto).

La salida de este código seria :1  ya que es el resto de dividir 10 sobre 3, sin tomar decimales.

Se pide al alumno analizar los códigos, y ver si puede anticipar y justificar la salida de los mismos.

A) B) C)

8.2. Operadores relacionales

Los operadores relacionales permiten determinar las relaciones que un valor o cantidad puede tener con otro.

Aquí resulta clave la idea de verdadero o falso. En C++ cualquier valor distinto de cero es verdadero, y cero (0) es falso. Así, las expresiones que utilizan operadores relacionales devolverán como resultado 0 si es falsa y 1 si es verdadera la expresión.
A continuación mostramos los operadores relacionales y su sintaxis:

OperadorSignificadoSintaxis
> mayor que <expresión 1> > <expresión 2>
< menor que <expresión 1> < <expresión 2>
>= mayor o igual que <expresión 1> >= <expresión 2>
<= menor o igual que <expresión 1> <= <expresión 2>
== igualdad <expresión 1> == <expresión 2>
!= desigualdad <expresión 1> != <expresión 2>
En las expresiones, "E1 <operador_relacional> E2, los operandos (E1, E2) tienen algunas restricciones, pero de momento nos conformaremos con que sean de tipo aritmético. El resto de las restricciones las veremos cuando conozcamos los punteros y los objetos.
Es un error frecuente utilizar el “=” en lugar del “==”, observar que el “=” es para ASIGNAR; y el “==” es para COMPROBAR LA IGUALDAD.
Si escribimos  a==3, lo que interpreta el lenguaje es  : ¿Es el valor de la variable a igual a 3?
Si escribimos a=3, lo que interpreta el lenguaje es: Asignar el valor de 3 a la variable a.

8.3. Operadores lógicos


Los operadores lógicos conforman expresiones lógicas y se utilizan para determinar cómo se presentan las relaciones entre las expresiones involucradas.
La siguiente tabla presenta los operadores lógicos y su sintaxis:

OperadorSignificadoSintaxis
&& AND o Y <expresión 1> && <expresión 2>
|| OR u O <expresión 1> || <expresión 2>
! NOT ! <expresión>

Operador && o AND

El operador "&&" equivale al "AND" o "Y"; devuelve "true" sólo si las dos expresiones evaluadas son "true" o distintas de cero, en caso contrario devuelve "false" o cero. Si la primera expresión evaluada es "false", la segunda no se evalúa.
Generalizando, con expresiones AND con más de dos expresiones, la primera expresión falsa interrumpe el proceso e impide que se continúe la evaluación del resto de las expresiones. Esto es lo que se conoce como "cortocircuito", y es muy importante, como veremos posteriormente.
El operador && se usa según la siguiente tabla de verdad, donde se representa “true” con 1 y “false” con 0.

Expresión 1Expresión 2<expresión 1> && <expresión 2>
true 1 true 1 true 1
true 1 false 0 false 0
false 0 true 1 false 0
false 0 false 0 false 0

Operador || u  OR

El operador "||" equivale al "OR" u "O inclusivo"; devuelve "true" si cualquiera de las expresiones evaluadas es "true" o distinta de cero, en caso contrario devuelve "false" o cero. Si la primera expresión evaluada es "true", la segunda no se evalúa.
El operador || se usa según la siguiente tabla de verdad, donde se representa  “true” con 1 y “false” con 0.

Expresión 1Expresión 2<expresión 1> || <expresión 2>
false 0 true 1 true 1
false 0 false 0 false 0

Operador ! o NOT

El operador "!" es equivalente al "NOT", o "NO", y devuelve "true" sólo si la expresión evaluada es "false" o cero; en caso contrario devuelve "false".
La expresión "!E" es equivalente a (0 == E).
El operador ! se usa según la siguiente tabla de verdad, donde se representa “true” con 1 y “false” con 0.

Expresión 1! Expresión 1
true 1 false 0
false 0 true 1

Se pide al alumno analizar el siguiente código y ver si puede anticipar la salida:

8.4. Operadores de asignación

La asignación consiste en un nombre de variable, seguido de un signo igual y el valor a ser asignado.
Por ejemplo:

    a = 14;
El operador asigna el valor de la izquierda (14) a la variable (a) que está a la derecha del operador asignación (=).

Existen varios operadores de asignación, el más evidente y el más usado es el "=", pero no es el único.
Los operadores de asignación y sus diferentes usos se describen a continuación:


Operador Descripción Uso Equivalente a
+= Suma y asignación
x+=y x=x+y
-= Resta y asignación x-=y x=x-y
*= Multiplicación y asignación
x*=y x=x*y
/= División y asignación
x/=y x=x/y
%= Resto y asignación
x%=y x=x%y


8.5. Operador "sizeof"

El operador “sizeof” es un operador del tiempo de compilación.
Este operador tiene dos usos diferentes. Devuelve el tamaño de la variable o tipo que está como operando. Si el operador funciona sobre un tipo de dato, éste deberá ir entre paréntesis.

Sintaxis:

   sizeof <expresión>
sizeof (nombre_de_tipo)

En ambos casos, el resultado es una constante entera que da el tamaño en bytes del espacio de memoria usada por el argumento, que es determinado por su tipo.
El espacio reservado por cada tipo depende de la plataforma, NO es el mismo para todos los equipos.
En el primer caso, el tipo del operando es determinado sin evaluar la expresión, y por lo tanto sin efectos secundarios.

Por ejemplo:
si el operando es de tipo "char", el resultado es 1.

A pesar de su apariencia, sizeof() NO es una función, sino un OPERADOR.

8.6. Operador condicional (?)

El operador "?:" se trata de un operador ternario (es decir tiene TRES operandos)

Sintaxis:

    <expresión lógica> ? <expresión> : <expresión>

En la expresión E1? E2:E3, primero se evalúa la expresión E1, si el valor es verdadero ("true"), se evaluará la expresión E2 y E3 será ignorada, si es falso ("false"), se evaluará E3 y E2 será ignorada.
Hay ciertas limitaciones en cuanto al tipo de los argumentos:

  • E1 debe ser una expresión lógica.
  • E2 y E3 deben ser de tipo aritmético.
  • E2 y E3 deben ser de estructuras o uniones compatibles.
  • E2 y E3 deben ser de tipo "void".

Veamos un ejemplo:

En este ejemplo usamos una función cin, esta pertenece a la librería iostream y se usa para ingresar por teclados valores que son almacenado en una varaible.

Se pide al alumno, correr el código, probar para la siguientes entradas de datos  y luego analizar la salida.

  • a=10.5  b=105.2  , cuanto valen las variables min y max?
  • a=105.3   b=10.2 cuanto valen las variables min y max?

En algunas programas se pueden llegar a observar este operador ternario en el #define.

    #define max (a,b) (((a) > (b)) ? (a) : (b))

De este ejemplo sólo nos interesa la parte de la derecha. La interpretación es: si "a" es mayor que "b", se debe evaluar "a", en caso contrario evaluar "b", en resumen, evalúa siempre el máximo!!

8.7. Operador coma (,)

El operador coma se utiliza para encadenar diversas expresiones. Provoca una secuencia de operaciones a realizar, se puede pensar como “hacer esto y luego esto”.
Tiene una doble función:  separa elementos de una lista de argumentos de una función.  Puede ser usado como separador en expresiones "de coma".
Ambas funciones pueden ser mezcladas, pero hay que añadir paréntesis para resolver las ambigüedades y evitar provocar errores, ya que el operador coma tiene precedencia más baja, por ejemplo, que el operador de asignación.

Sintaxis:

    E1, E2, ... , En

En una expresión "de coma", cada operando es evaluado como una expresión, pero los resultados obtenidos se tienen en cuenta en la próxima evaluación.

Por ejemplo:

    x = (y=3,y+1);

En primer lugar asigna el valor 3 a la variable y, y después asigna el valor 4 a la variable x.

9. Expresiones

La combinación de variables, constantes definidas o números con uno o más operadores dan como resultado un valor.

Esta combinación de variables, constantes y operadores recibe el nombre de expresión.
Una expresión es, según el diccionario, un "conjunto de términos que representan una cantidad", entre nosotros es cualquier conjunto de operadores y varios operando, que dan como resultado una cantidad.
Operando es cada una de las cantidades, constantes, variables o expresiones que intervienen en una expresión.
Existe una división, en los operadores, atendiendo al número de operando que afectan. Según esta clasificación pueden ser unitarios, binarios o ternarios, los primeros afectan a un solo operando, los segundos a dos y los ternarios como era de esperar a tres.
Las variables y constantes se pueden procesar utilizando operaciones y funciones adecuadas a sus tipos.
Cada expresión toma un valor que se determina tomando los valores de las variables y constantes implicadas y la ejecución de las operaciones indicadas.

Las expresiones se pueden clasificar, según los tipos de objetos que manipulan, en:

  • Aritméticas: cuyo resultado sería de tipo numérico.
  • Lógicas: cuyo resultado sería de tipo lógico.
  • Carácter: cuyo resultado sería de tipo carácter.

9.1. Expresiones aritméticas

Una expresión aritmética es un conjunto de variables y/o constantes unidas o relacionadas por paréntesis y operadores aritméticos.
Son análogas a las fórmulas matemáticas. Las variables y constantes son numéricas (enteras o punto flotante) y las operaciones son aritméticas.

Por ejemplo:

    sueldo = sueldo_base + 0.15 * monto_ventas
e = a*b*b / 3 + (a*a + b) / (b + c)

Cuando se utilizan expresiones aritméticas se debe tener en cuenta que:

  • Si en una operación ambos operando son enteros, entonces el resultado de la operación es un entero.
  • Si en una operación uno o ambos operando son reales, entonces el    resultado de la operación es un real.
  • El operador “/” produce un cociente entero si los dos operando son enteros. Esto significa que se pierde la parte decimal si la división no es exacta.
  • El operador “/” produce un cociente float si uno o los dos operando son float.
Por ejemplo:
7 / 2 es igual a 3 y no 3.5 como lo es matemáticamente. Esto debido a que 7 y 2 son enteros y al dividir dos enteros se pierde la parte fraccionaria, no se redondea.
En cambio:
7.0 / 2 es igual a 3.5 ya que si uno o los dos operando son reales, entonces el resultado es real. En este caso 7.0 es real.

Reglas de Precedencia

Las expresiones que tienen dos o más operando requieren reglas matemáticas que permitan determinar el orden de las operaciones.
Las Reglas de Prioridad o Precedencia son las reglas matemáticas que permiten determinar el orden de las operaciones.
Son:
  • Las operaciones que están encerradas entre paréntesis se evalúan primero. Si aparecen varios paréntesis anidados, se evalúan primero los paréntesis interiores.
  • En caso de coincidir varios operadores de igual prioridad, el orden se determina de izquierda a derecha.
  • Las operaciones aritméticas dentro de una expresión suelen seguir el siguiente orden de prioridad:
Categoría
Operadores
Sufijos () [] -> . ++ --
Unarios + - ! ~ ++ -- (type)* & sizeof
Multiplicativos * / %
Aditivos + -
Desplazamiento << >>
Relacionales < <= > >=
Bit AND &
Bit XOR ^
Bit OR |
AND lógica &&
OR lógica ||
Condicional ?:
Asignación = += -= *= /= %=>>= <<= &= ^= |=
Coma ,


9.2. Expresiones lógicas

Una expresión lógica o booleana es un conjunto de variables y/o constantes unidas mediante operadores lógicos y operadores relacionales.
Las expresiones lógicas se forman combinando constantes lógicas, variables lógicas y otras expresiones lógicas, utilizando operadores lógicos y relacionales; y su valor siempre es verdadero o falso.
Una expresión lógica solo puede tomar uno de dos valores: verdadero o falso (booleano SOLAMENTE).
Las expresiones lógicas son ampliamente utilizadas en las estructuras selectivas y las estructuras repetitivas.

Expresiones comparativas

El formato general para las comparaciones es:
<Expresión1> operador de relación <Expresión2>

y el resultado será verdadero o falso.

Por Ejemplo:
    int A = 4
    int B = 3
    A > B //da como resultado Verdadero
    (A – 2) < (B – 4) //da como resultado Falso.

Los operadores de relación se pueden aplicar a los tipos de datos estándar: entero, punto flotante, carácter o lógico.

Expresiones lógicas

En las expresiones lógicas se pueden mezclar operadores de relación y lógicos.

Por Ejemplo:
(1 < 5) and (5 < 10)    da como resultado Verdadero.
(5 < 10) or (‘A’ < ‘B’)    da como resultado Verdadero.