En programación, la conversión de tipos, también llamada casting, es el proceso mediante el cual podemos cambiar el tipo de un dato por otro.
Hay veces en las que necesitarás tipos concretos para realizar ciertas operaciones y si los datos de que dispones son de otro tipo, los resultados no serán los esperados.
Es muy importante que entiendas cómo funciona el casting en Arduino y cómo hacerlo de forma correcta.
Conversión de tipos implícita
La conversión de tipos implícita, es el primer tipo de conversión que debes conocer. Este tipo no depende de ti, es el lenguaje quién hace la conversión cuando lo necesita. Por eso se llama implícita.
Para que lo entiendas mejor te lo voy a explicar con un ejemplo, que siempre es más fácil.
Mira el siguiente código:
void setup() {
Serial.begin(9600);
int a = 10;
float b = 3.5;
float resultado = a * b;
Serial.println(resultado);
}
void loop() {
}
He puesto todo el código dentro de la función setup() porque solo quiero ejecutarlo una vez. Recuerda que la función loop() es un bucle infinito.
He declarado 2 variables:
int a = 10;
float b = 3.5;
La variable a es de tipo entero (int) y la variable b es de tipo punto flotante (float).
Ahora fíjate en la siguiente instrucción:
float resultado = a * b;
Estoy multiplicando la variable a por la variable b. Ambas son de tipo diferente, pero la multiplicación es una operación que necesita que ambas sean del mismo tipo. ¿Qué hace Arduino en este caso? Una conversión de tipo implícita: convierte el int en float sin que nosotros tengamos que hacer nada. El resultado final será de tipo float, por eso la variable resultado es de ese tipo.
Vamos a ver otro ejemplo:
void setup() {
Serial.begin(9600);
byte a = 200;
int b = 0;
b = a;
Serial.println(b);
}
void loop() {
}
En este ejemplo la variable a es de tipo byte y la variable b es de tipo int:
byte a = 200;
int b = 0;
Al asignar un valor byte (a) a una variable int, Arduino hace la conversión implícita de byte a int para que el dato se pueda almacenar correctamente:
b = a;
IMPORTANTE: En estos dos ejemplos las conversiones de tipos que se están realizando son entre tipos compatibles, por lo que la conversión no dará problemas. Arduino siempre hace conversiones implícitas entre tipos compatibles. Si los tipos fuesen incompatibles podría producirse una pérdida de precisión en los datos e incluso un error. Veremos esto en detalle en dos últimos apartados.
Conversión de tipos explícita
En el caso de la conversión de tipos explícita, es el programador quien indica explícitamente en el código que se debe hacer un cambio de tipo. Por eso se llama explícita.
El lenguaje de programación de Arduino proporciona algunos mecanismos que permiten realizar este tipo de conversión. Puedes verlos en la siguiente tabla:
Convierte el dato a | |
(tipo) dato | tipo indicado entre paréntesis |
byte(dato) | byte |
char(dato) | char |
float(dato) | float |
int(dato) | int |
long(dato) | long |
word(dato) | word |
A continuación puedes ver un ejemplo de uso con cada uno de ellos.
(tipo)
Este método lo puedes usar para hacer cualquier conversión. Simplemente debes indicar el nuevo tipo entre paréntesis delante del dato que quieres convertir.
Vamos a ver algunos ejemplos para que lo entiendas mejor.
En este ejemplo vamos a convertir un int en un unsigned int:
void setup() {
Serial.begin(9600);
int a = 100;
unsigned int b = 0;
b = (unsigned int)a;
Serial.println(b);
}
void loop() {
}
Como puedes ver, la variable a es de tipo int y tiene valor inicial 100. La variable b es de tipo unsigned int y es la variable destino en la que guardaremos el dato tras hacer la conversión de tipo.
Para convertir el dato int a unsigned int basta con colocar (unsigned int) delante del dato que se quiere convertir, en este caso la variable a. Es obligatorio ponerlo entre paréntesis:
b = (unsigned int)a;
Si quieres, prueba a cambiar el tipo de la variable a por otro y haz la conversión a unsigned int, a ver qué pasa. Si el tipo es compatible con unsigned int la conversión se realizará correctamente, sino, puede que te lleves una sorpresa. ¿Y si la variable a tuviese valor negativo? Te explicaré esto en el apartado Conversión entre tipos incompatibles.
Ahora vamos a convertir un short a un int:
void setup() {
Serial.begin(9600);
short a = 123;
int b = 0;
b = (int)a;
Serial.println(b);
}
void loop() {
}
La variable a contiene un dato de tipo short con valor 123. La variable b es de tipo int. En esta segunda variable es donde se almacenará el resultado de la conversión.
Para hacer el casting basta con poner el nuevo tipo entre paréntesis delante de la variable a. Tal y como hicimos en el ejemplo anterior:
b = (int)a;
De nuevo haz pruebas con diferentes tipos, a ver qué pasa.
Recuerda, entre paréntesis debes indicar el nuevo tipo al que quieres hacer la conversión.
byte()
Arduino proporciona algunas funciones que permiten realizar cambios de tipo concretos. La función byte() es una de ellas.
Para este ejemplo usaremos una variable de tipo char y la convertiremos a byte usando la función:
void setup() {
Serial.begin(9600);
char a = 'A';
byte b = 0;
b = byte(a);
Serial.println(b);
}
void loop() {
}
He elegido estos dos tipos porque son muy compatibles. Ambos ocupan lo mismo en memoria: 8 bits.
Como puedes ver en el código, la variable a (char) tiene valor ‘A’. Es un carácter. Al hacer la conversión de tipo a byte, el resultado es el valor ASCII de ese carácter, en este caso el 65 (ASCII del caracter ‘A’).
Fíjate que ahora no escribo el nuevo tipo entre paréntesis como en los ejemplos anteriores (aunque también se puede hacer). Lo que hago es utilizar la función byte() que nos proporciona Arduino:
b = byte(a);
La función byte() recibe la variable a (char) como parámetro y devuelve como resultado el dato convertido a byte.
De nuevo te animo a que cambies el tipo y valor de la variable a e intentes convertirlo a byte utilizando la función para que veas qué ocurre.
char()
Este ejemplo es justo el contrario al anterior. Vamos a convertir un valor ASCII numérico almacenado en una variable de tipo byte, el 65 en este caso, a un carácter (char):
void setup() {
Serial.begin(9600);
byte a = 65;
char b = 0;
b = char(a);
Serial.println(b);
}
void loop() {
}
El valor 65 en ASCII equivale al carácter ‘A’, por lo tanto, al hacer la conversión a char el resultado que se almacenará en la variable b será ‘A’.
Para hacer la conversión a char usamos la función char() que nos proporciona Arduino, aunque también puedes usar la técnica de los paréntesis que vimos antes:
b = char(a);
En este caso, la función char() recibe como parámetro una variable de tipo byte. El resultado devuelto es de tipo char y se almacena en la variable b.
De nuevo haz pruebas con otros tipos para que veas cuáles puedes convertir en char.
float()
Ahora vamos a convertir un dato de tipo int a float:
void setup() {
Serial.begin(9600);
int a = 180;
float b = 0.0;
b = float(a);
Serial.println(b);
}
void loop() {
}
Ten en cuenta que vamos a pasar de un entero a un punto flotante. Son tipos de datos bastante diferentes, pero son compatibles en ese sentido, en el sentido contrario no (de float a int), pero eso te lo contaré al final del artículo: Conversión entre tipos incompatibles.
De nuevo utilizamos una función que nos proporciona Arduino para hacer la conversión. La función float() puede recibir cualquier tipo y convertirlo a float:
b = float(a);
En este caso el tipo recibido es un int (variable a) y el resultado se almacena en la variable b que es de tipo float.
int()
Toca el turno del int. Vamos a convertir un dato de tipo char a entero:
void setup() {
Serial.begin(9600);
char a = 'A';
int b = 0;
b = int(a);
Serial.println(b);
}
void loop() {
}
El resultado que obtendremos será muy similar al que vimos cuando pasamos de char a byte. La diferencia es que int ocupa más en memoria que un byte.
La conversión devolverá el valor ASCII numérico del caracter ‘A’, es decir, un 65. Quedará almacenado en la variable b:
b = int(a);
Para convertir un tipo de dato a int Arduino nos proporciona la función int(), pero también permite hacer el casting con los paréntesis (b = (int)a).
¿Qué crees que pasará si cambias el tipo de la variable a por float e intentas convertirlo a int? Pruébalo.
long()
En este ejemplo vamos a ver cómo convertir un int a long:
void setup() {
Serial.begin(9600);
int a = 32562;
long b = 0;
b = long(a);
Serial.println(b);
}
void loop() {
}
La conversión a long es uno de los casos más sencillos, puesto que es uno de los tipos más grandes y puedes convertir prácticamente cualquier dato a long.
Para convertir un dato a long, Arduino nos proporciona la función long(). Pasa entre paréntesis un dato de cualquier tipo y te devolverá el dato convertido a long.
En el ejemplo estamos haciendo la conversión de tipo de int a long:
b = long(a);
word()
Y hemos llegado al último caso. La conversión al tipo word:
void setup() {
Serial.begin(9600);
int a = 2457;
word b = 0;
b = word(a);
Serial.println(b);
}
void loop() {
}
En este caso estamos convirtiendo int a word. Para ello usamos la función word() que nos proporciona Arduino de la misma forma que vimos en los casos anteriores:
b = word(a);
Prueba a cambiar la variable a por otros tipos para comprobar si también se pueden convertir a word.
Conversión entre tipos compatibles
Arduino permite hacer prácticamente cualquier conversión de tipo, pero debes tener en cuenta que algunos tipos son compatibles entre si, lo que te garantiza que el cambio se realice de forma correcta.
En la siguiente tabla puedes consultar qué tipos son compatibles.
En la primera columna tienes el nuevo tipo, es decir, el tipo en el que quieres convertir el dato. Y en la segunda columna tienes todos los tipos posibles desde los que hacer la conversión, es decir, el tipo de dato del que partes y quieres convertir en el tipo de la primera columna.
Nuevo tipo | Tipos origen compatibles |
byte | char |
short | byte, char, word, int* |
int | byte, short, char, word |
long | byte, short, char, word, int |
word | byte, short, char, int* |
float | byte, short, char, int, long |
double | byte, short, char, int, long, float |
char | byte |
unsigned int | byte, short, char, int ** |
unsigned long | byte, short, char, int ** |
* En algunos modelos de Arduino los tipos int, short y word ocupan lo mismo en memoria, pero hoy otros modelos en los que el tipo int ocupa el doble, por lo que, dependiendo de la placa que estés usando la conversión funcionará correctamente o te dará algún problema.
** Solo son compatibles los valores positivos, puesto que la conversión se estaría realizando a un tipo sin signo (unsigned). Los valores negativos darían resultados inesperados.
Básicamente, los tipos compatibles son los que utiliza Arduino cuando hace una conversión implícita. Si tu también los usas sabrás que la conversión se realizará correctamente.
Experimenta con los métodos que vimos anteriormente y haz conversiones con los tipos de la tabla, verás que el resultado es el esperado y no perderás información en ningún caso.
Conversión entre tipos incompatibles
En la tabla del punto anterior vimos que tipos de datos eran compatibles entre si. Esos tipos se pueden convertir sin problema. pero ¿Qué pasa con el resto de combinaciones? ¿Podemos convertir un float a int? ¿O un long a word?
La respuesta a todas estas preguntas es si, PERO. Y pongo el pero en mayúscula porque tienes que tener mucho cuidado cuando hagas este tipo de conversión porque puedes perder información.
Mira el siguiente código:
void setup() {
Serial.begin(9600);
float a = 56.87;
int b = 0;
b = (int)a;
Serial.println(b);
}
void loop() {
}
Por un lado tenemos la variable a de tipo float con valor 56.87. Por otro lado, tenemos una variable entera con valor 0.
Después tenemos una conversión de tipo de float a int:
b = (int)a;
¿Cuál crees que será el resultado guardado en b? Ya te adelanto que 56.87 no. Si conviertes una variable con punto flotante a entero perderás la parte decimal porque no puedes almacenar decimales en un int. En este caso la variable b tomará valor 58.
Lo mismo pasaría si intentas convertir un double a long, por ejemplo.
Te voy a poner otro ejemplo:
void setup() {
Serial.begin(9600);
int a = 2345;
byte b = 0;
b = (byte)a;
Serial.println(b);
}
void loop() {
}
En este caso tenemos una variable int (a) y otra byte (b). La variable a tiene valor 2345 y la estamos intentando convertir a byte:
b = (byte)a;
¿Qué crees que pasará? Pues que 2345 es un valor demasiado grande para almacenarlo en un byte. Recuerda que el tipo byte solo acepta valores entre o y 255.
Hacer esta conversión de tipo no te dará error, pero como son tipos incompatibles el resultado no será el esperado. Es más, el resultado es 41, que no tiene nada que ver con el valor original que era 2345. Hemos perdido mucha información en el proceso.
¿Y si hay valores negativos? Mira este último ejemplo:
void setup() {
Serial.begin(9600);
int a = -2345;
unsigned int b = 0;
b = (unsigned int)a;
Serial.println(b);
}
void loop() {
}
Tenemos un número negativo entero (int) que estamos convirtiendo a entero sin signo (unsigned int). El resultado obtenido tras la conversión es 63191, que como puedes comprobar no se parece en nada a -2345. Si lo que buscabas haciendo esto era quitar el signo para quedarte con 2345, mejor multiplícalo por -1.
¿Significa esto que nunca debes hacer conversiones entre tipos incompatibles? Pues no. Hay casos en los que pueden ser útiles, por ejemplo si quieres extraer la parte entera de un número con punto flotante. Como vimos en el ejemplo anterior, al hacer la conversión al tipo int perdíamos la parte decimal y conservábamos la entera. Y habrá más casos en los que te pueda resultar útil, pero serán casos muy concretos. Siempre que hagas conversiones de tipos incompatibles debes hacerlo con cuidado y prestar mucha atención a los resultados.