Las Matem谩ticas en Arduino

A la hora de programar en entorno Arduino, a menudo tenemos que hacer operaciones matem谩ticas. Por ejemplo, calcular la distancia a la que se encuentra un objeto gracias a la f贸rmula de la velocidad del sonido en el aire. Vamos a explicar en esta lecci贸n algunas nociones de Aritm茅tica en Arduino.


Las operaciones matem谩ticas en Arduino.

Una de las principales funciones del microcontrolador es realizar c谩lculos, tanto con n煤meros directamente como con los valores de variables. Comencemos nuestra inmersi贸n en el mundo de las matem谩ticas con las acciones m谩s simples:

  • = asignaci贸n
  • % resto de la divisi贸n
  • * multiplicaci贸n
  • / division
  • + adici贸n
  • – resta

Veamos un ejemplo simple:

int a = 10;
int b = 20;
int c = a + b; // c = 30
int d = a * b; // d = 200
// esto tambi茅n es posible
d = d / a;     // d = 20
c = c * d;     // c = 600

Con respecto a las dos 煤ltimas l铆neas del ejemplo, cuando una variable est谩 involucrada en el c谩lculo de su propio valor: tambi茅n hay operadores compuestos que acortan el registro:

  • + =  adici贸n compuesta: a + = 10 equivalente a: a = a + 10
  • – =  resta compuesta: a – = 10 equivalente a: a = a – 10
  • * = multiplicaci贸n compuesta: a * = 10 equivalente a: a = a * 10
  • / =  divisi贸n compuesta: a / = 10 equivalente a: a = a / 10
  • % = suma el resto de la divisi贸n: a% = 10 equivalente a: a = a + a% 10

Utiliz谩ndolos, puede acortar la grabaci贸n de las dos 煤ltimas l铆neas del ejemplo anterior:

d / = a;     // (equivalente a d = d / a) d = 20
c * = d;     // (equivalente a c = c * d) c = 600

Muy a menudo en programaci贸n, se usa la suma o resta de uno, (incremento) para lo cual tambi茅n hay una breve notaci贸n:

  • ++ (m谩s m谩s) incremento: a ++ equivalente a: a = a + 1
  • – – (menos menos) decremento: a– equivalente a: a = a – 1

El orden en el que se escribe el incremento es muy importante: post-incremento var ++ devuelve el valor de var antes de que se ejecutara el incremento. La operaci贸n de preincremento ++ var devuelve el valor de una variable ya modificada. Ejemplo:

byte a, b;
a = 10;
b = a ++;
// a obtendr谩 el valor 11
// b se establecer谩 en 10
a = 10;
b = ++ a;
// a obtendr谩 el valor 11
// 隆b se establecer谩 en 11!

Como se mencion贸 en la lecci贸n anterior, es recomendable inicializar las variables, de lo contrario pueden tener un valor aleatorio y se obtendr谩 un resultado impredecible en las operaciones matem谩ticas. Si una variable al inicio de la ejecuci贸n del programa, o despu茅s de una llamada de funci贸n (variable local) debe tener un valor de 0, inicial铆cela como 0.

byte a;     // declarar sin inicializaci贸n
byte b = 0; // declarar y asignar 0
a ++; // resultado impredecible
b ++; // definitivamente resultar谩 en 1

Orden de Calculo.

El orden de evaluaci贸n de las expresiones obedece a las reglas matem谩ticas habituales: primero se realizan las acciones entre par茅ntesis, luego la multiplicaci贸n y la divisi贸n, y finalmente la suma y la resta.


Velocidad de computaci贸n.

Los c谩lculos realizados toman alg煤n tiempo del procesador, depende del tipo de acci贸n y del tipo de datos con los que se realiza la acci贸n. Debe comprender que no todas las acciones en todos los casos pasan tanto tiempo como se describir谩 m谩s adelante: el compilador intenta optimizar los c谩lculos tanto como sea posible, ya que lo hace, puede intentar buscar en Internet. Los c谩lculos optimizados toman un tiempo insignificante en comparaci贸n con los no optimizados. Es dif铆cil decir si se optimizar谩 un solo c谩lculo en su c贸digo, por lo que siempre debe prepararse para lo peor y saber c贸mo hacerlo mejor. A saber:

  • Arduino (en AVR) no tiene soporte de 芦hardware禄 para c谩lculos de punto flotante (float), y estos c谩lculos se realizan usando herramientas separadas por software y toman mucho m谩s tiempo que con tipos enteros
  • Cuanto m谩s 芦masivo禄 sea el tipo de datos, m谩s tiempo se tarda en calcular; las acciones con variables de 1 byte se realizan mucho m谩s r谩pido que con las de 4 bytes
  • La divisi贸n (y la b煤squeda del resto de la divisi贸n) se realiza en m贸dulos separados (como operaciones flotantes), por lo que esta operaci贸n lleva m谩s tiempo que la suma / resta / multiplicaci贸n. Para optimizar la velocidad de los c谩lculos, tiene sentido reemplazar la divisi贸n por multiplicaci贸n por el n煤mero rec铆proco (incluso un float)

Resumiendo todo lo anterior, quiero mostrarles una tabla de este tipo, que muestra el tiempo de los聽c谩lculos de varios tipos de datos que聽no聽est谩n聽optimizados por el聽compilador, el tiempo se indica en microsegundos (渭s)聽para una frecuencia de reloj de 16 MHz..聽La operaci贸n de divisi贸n tambi茅n incluye al resto de la divisi贸n,聽%:

Tipo de datosTiempo de ejecuci贸n, 渭s
Adici贸n y sustracci贸nMultiplicaci贸nDivisi贸n, resto
int8_t0.440,62514.25
uint8_t0.440,6255.38
    
int16_t0,891.37514.25
uint16_t0,891.37513.12
int32_t1,756.0638,3
uint32_t1,756.0637,5
    
float8.1251031,5
Tiempo de los c谩lculos de varios tipos de datos.

Esta informaci贸n se proporciona 煤nicamente con fines informativos y no necesita preocuparse por la velocidad de los c谩lculos, ya que la mayor铆a de ellos estar谩n optimizados.  Para proyectos simples sin miles de c谩lculos, honestamente, no se preocupe =)


Desbordamiento de variable.

Desde que comenzamos a hablar de acciones que aumentan o disminuyen el valor de una variable, 驴vale la pena pensar en qu茅 pasar谩 con la variable si su valor se sale del rango aceptable? Aqu铆 todo es bastante simple: en caso de un desbordamiento hacia arriba, el valor m谩ximo de la variable se recorta del nuevo valor grande y solo tiene el resto. A modo de comparaci贸n, imaginemos una variable como un cubo. Asumiremos que al verter agua y llenar en exceso el balde, diremos detenerse, verter toda el agua y agregar el resto. Entonces, con la variable, lo que queda permanecer谩. Si el desbordamiento ocurre varias veces, vaciaremos nuestro 鈥渂alde鈥 varias veces y a煤n dejaremos el resto. Otro buen ejemplo es la taza pitag贸rica…. El desbordamiento en la direcci贸n opuesta, es decir en caso negativo, vierta el agua, asumiremos que el cubo est谩 completamente lleno. S铆, es cierto =) Veamos un ejemplo:

/  tipo de datos byte
// min. valor 0
// m谩x. valor 255
byte val = 255;
// val se convierte en 0 aqu铆
val ++;
// y luego de cero se convierte en 246
val - = 10;
// 隆Desbordamiento! Restos 13
val = 525;
// y viceversa: val es 236
val = -20;

N煤meros mayores, int y long.

Para la suma y la resta, el tipo predeterminado es long (4 bytes), pero para la multiplicaci贸n y la divisi贸n se usa int(2 bytes), lo que puede dar lugar a resultados impredecibles. Si, al multiplicar n煤meros, el resultado excede 32’768, se calcular谩 incorrectamente. Para corregir la situaci贸n, debe escribir (tipo de datos) antes de la multiplicaci贸n, lo que obligar谩 al MC a asignar memoria adicional para el c谩lculo (por ejemplo( long ) 35 * 1000). Tambi茅n hay modificadores que hacen aproximadamente lo mismo.

  • u o U – convertir谩 a formato unsigned int (de 0 a 65’535). Ejemplo:36000u
  • l o L – convertir谩 a formato long (-2 147 483 648 … 2 147 483 647). Ejemplo: 325646L
  • ul o UL – convertir谩 a formato unsigned long (de 0 a 4.294.967.295). Ejemplo: 361341ul

Veamos c贸mo funciona esto en la pr谩ctica:

val long ;
val = 2.000.000.000 + 6.000.000;        // calcular correctamente (desde la suma)
val = 25 * 1000;                   // calcular correctamente (multiplicaci贸n, menos de 32'768)
val = 35 * 1000;                   // calcular谩 INCORRECTO! (la multiplicaci贸n es mayor que 32'768)
val = ( long ) 35 * 1000;             // calcular谩 correctamente (asignar谩 memoria (larga))
val = 35 * 1000L;                  // calcular correctamente (modificador L)
val = 35 * 1000u;                  // calcular谩 correctamente (modificador u)
val = 70 * 1000u;                  // calcula INCORRECTO (modificador u, resultado> 65535)
val = 1000 + 35 * 10 * 100;        // calcular谩 INCORRECTO! (en multiplicaci贸n por 32'768)
val = 1000 + 35 * 10 * 100L;       // 隆calcula correctamente! (modificador L)
val = ( long ) 35 * 1000 + 35 * 1000; // calcular谩 INCORRECTO! La segunda multiplicaci贸n lo estropea todo
val = ( long ) 35 * 1000 + ( long ) 35 * 1000; // calcular谩 correctamente (asignar谩 memoria (larga))
val = 35 * 1000L + 35 * 1000L;     // calcular correctamente (modificador L)

Trabajando en coma flotante.

Arduino admite n煤meros de punto flotante (decimales). Este tipo de datos no tiene soporte de hardware, pero est谩 implementado en software, por lo que los c谩lculos con 茅l toman varias veces m谩s tiempo que con un tipo entero, puede ver esto en la tabla anterior. Adem谩s de la computaci贸n lenta, el soporte para trabajar con flotantes ocupa memoria, porque se implementa como una 芦biblioteca禄. El uso de operaciones matem谩ticas con float ( * / + – ) agrega aproximadamente 1000 bytes a la memoria flash, de una vez, simplemente conecte la herramienta para realizar la acci贸n.

Arduino admite tres tipos de entrada de punto flotante:

Tipo de DatoEjemploLo que es igual
Decimal20,520,5
Cient铆fico2.34E52,34 * 10 ^ 5 o 234000
Ingenieria67e-1267 * 10 ^ -12 o 0,000000000067

Existe una peculiaridad con los c谩lculos: si la expresi贸n no contiene n煤meros聽float, entonces los c谩lculos tendr谩n un resultado entero (la parte fraccionaria se聽corta).聽Para obtener el resultado correcto, debe escribir la transformaci贸n (float)聽antes de la acci贸n, use聽n煤meros float o variables聽float.聽Tambi茅n hay un modificador聽f聽que solo se puede aplicar a n煤meros float.聽No tiene sentido, pero se puede encontrar tal registro.聽Veamos:

float val;             // adem谩s asignaremos 100/3, esperamos el resultado 33.3333
val = 100/3;         // calcula INCORRECTO (resultado 33.0)
int val1 = 100;        // variable entera
val = val1 / 3;        // calcula INCORRECTO (resultado 33.0)
float val2 = 100;      // variable flotante
val = val2 / 3;        // contar谩 correctamente (hay una variable float)
val = ( float ) 100/3 ;  // calcular correctamente (especificar (float))
val = 100,0 / 3;       // cuenta correctamente (hay un float)
val = 100 / 3,0 f;      // calcular谩 correctamente (hay un float y un modificador)

En el asignamiento de n煤meros聽float a un tipo de datos entero, la聽parte fraccionaria se trunca.聽Si desea redondeo matem谩tico, debe usarlo aparte:

int val;
val = 3,25 ;        // val se convierte en 3
val = 3,92 ;        // val se convierte en 3
val = round ( 3,25 ) ; // val se convierte en 3
val = round ( 3,92 ) ; // val se convierte en 4

El siguiente punto importante: debido a la peculiaridad del modelo de 芦n煤meros de punto flotante禄, los c谩lculos a veces se realizan con un peque帽o error.

float val2 = 1 .1 - 1.0 ;
// val2 == 0.100000023 !!!
float val4 = 1,5 - 1,0 ;
// val4 == 0.500000000

Parece que, val2 deber铆a volverse parejo 0,1 despu茅s de la resta, pero en el octavo d铆gito hubo un error. Tenga mucho cuidado al comparar n煤meros float, especialmente con operaciones estrictas == , > = y <= : el resultado puede ser incorrecto e il贸gico.


Funciones matem谩ticas en math.h

Funci贸n Descripci贸n
cos ( x ) Coseno (radianes)
sin( x ) Seno (radianes)
tan( x )Tangente (radianes)
fabs ( x )M贸dulo para n煤meros flotantes
fmod ( x, y ) Resto de x por y para float
modf ( x, * iptr ) Devuelve la parte fraccionaria, almacena el entero en iptr
modff ( x, * iptr ) Lo mismo, pero para float
sqrt ( x ) Ra铆z cuadrada
sqrtf ( x ) Ra铆z cuadrada para n煤meros flotantes
cbrt ( x ) Ra铆z c煤bica
hypot ( x, y )Hipotenusa (ra铆z (x * x + y * y))
square( x )Cuadrado (x * x)
floor( x )Redondea al n煤mero entero m谩s cercano
ceil( x )Redondea al n煤mero entero m谩s cercano
frexp ( x, * pexp )
ldexp ( x, exp ) x * 2 ^ exp
exp ( x ) Exponente (e ^ x)
cosh ( x ) Coseno hiperb贸lico (radianes)
sinh ( x ) Seno hiperb贸lico (radianes)
tanh ( x ) Tangente hiperb贸lica (radianes)
acos ( x ) Arccosine (radianes)
asin ( x ) Arcoseno (radianes)
atan ( x ) Arco tangente (radianes)
atan2 ( y, x ) Arco tangente (y / x) (encuentra el cuadrante en el que se encuentra el punto)
log( x )Logaritmo natural de x (ln (x))
log10 ( x ) Logaritmo decimal de x (log_10 x)
pow ( x, y ) Grado (x ^ y)
isnan ( x ) Comprobando nan (1 s铆, 0 no)
isinf ( x ) Regreso 1 si x + infinito, 0 si no
isfinite ( x )Devuelve distinto de cero solo si el argumento es finito
copysign ( x, y ) Devuelve con signo x y (el signo es + -)
signbit ( x )Devuelve distinto de cero solo si _X es negativo
fdim ( x, y ) Devuelve la diferencia entre xey si x es mayor que y, 0 en caso contrario
fma ( x, y, z ) Devuelve x * y + z
fmax ( x, y ) Devuelve el mayor de los n煤meros
fmin ( x, y ) Devuelve el menor de los n煤meros
trunc ( x ) Devuelve la parte entera de un n煤mero con punto decimal.
round( x )Redondeo matem谩tico
lround( x )Redondeo matem谩tico (para n煤meros grandes)
lrint( x )Redondea el valor de punto flotante especificado al valor entero m谩s cercano utilizando el modo y la direcci贸n de redondeo actuales
Funciones matem谩ticas de la librer铆a math.h

Funciones Arduino.

Funci贸nValor
min ( a, b )Devuelve el menor de los n煤meros a y b
max ( a, b )Devuelve el mayor de los n煤meros
abs ( x )Valor absoluto de un n煤mero
constrain ( val, min, max )L铆mite de rango de n煤meros val Entre min y max
map ( val, min, max, newMin, newMax )Convertir rango de n煤meros聽val聽(de聽min聽antes de聽max) a un nuevo rango (desde聽newMin聽antes de聽newMax).聽val = map ( analogRead ( 0 ) , 0, 1023, 0, 100 ) ;– obtenemos los valores 0-100 en lugar de 0-1023 de la entrada anal贸gica.聽隆Solo funciona con n煤meros enteros!
round( x )Redondeo matem谩tico
radians (deg)Convertir grados a radianes
degrees( rad )Conversi贸n de radianes a grados
sq( x )N煤mero cuadrado
Funciones Arduino

Constantes Matem谩ticas.

ConstanteValorDescripci贸n
INT8_MAX127Max. valor char, int8_t
UINT8_MAX255Max. valor de byte, uint8_t
INT16_MAX32767Max. valor int, int16_t
UINT16_MAX65535Max.聽valor int sin signo, uint16_t
INT32_MAX2147483647Max. valor largo, int32_t
UINT32_MAX4294967295Max.聽valor largo sin signo, uint32_t
M_E2.718281828N煤mero e
M_LOG2E1.442695041log_2 e
M_LOG10E0,434294482log_10 e
M_LN20.693147181log_e 2
M_LN102.302585093log_e 10
M_PI3.141592654Pi
M_PI_21.570796327pi / 2
M_PI_40,785398163pi / 4
M_1_PI0.3183098861 / pi
M_2_PI0,6366197722 / pi
M_2_SQRTPI1.1283791672 / ra铆z (pi)
M_SQRT21.414213562ra铆z (2)
M_SQRT1_20,7071067811 / ra铆z (2)
NAN__builtin_nan (芦禄)NAN
INFINITY__builtin_inf ()infinito
Pi3.141592654Pi
HALF_PI1.570796326medio Pi
TWO_PI6.283185307dos pi
EULER2.718281828N煤mero e de Euler
DEG_TO_RAD0,01745329Conversi贸n grados constante a rad
RAD_TO_DEG57.2957786Conversi贸n radianes a grados
Constantes matem谩ticas.

Deja un comentario