Tips de lenguaje C

Mi opción de ahorro de memoria favorito es la de evitar que el compilador agregue las rutinas de punto flotante al código final. Ocupan muchísimo, y buena parte de las veces podemos simularlo con operaciones en variables enteras.

Por ejemplo, si tenemos un sensor de temperatura, y como mucho nos va a dar 120 °C, y queremos usar dos dígitos decimales en la presentación, resulta que podemos hacer todas las operaciones en un entero (dos bytes) ya que el valor más alto que tenemos que guardar es 12 000. Solo hay que ajustar un poco la rutina de presentación, para la colocación del «.» entre la parte entera y la decimal.

De hecho, esto es tan frecuente que algunos compiladores lo "traen de serie", como el caso del CSS y su declarador de tipos _fixed().
 
Mi opción de ahorro de memoria favorito es la de evitar que el compilador agregue las rutinas de punto flotante al código final. Ocupan muchísimo, y buena parte de las veces podemos simularlo con operaciones en variables enteras.

Por ejemplo, si tenemos un sensor de temperatura, y como mucho nos va a dar 120 °C, y queremos usar dos dígitos decimales en la presentación, resulta que podemos hacer todas las operaciones en un entero (dos bytes) ya que el valor más alto que tenemos que guardar es 12 000. Solo hay que ajustar un poco la rutina de presentación, para la colocación del «.» entre la parte entera y la decimal.

De hecho, esto es tan frecuente que algunos compiladores lo "traen de serie", como el caso del CSS y su declarador de tipos _fixed().
Eso lo he visto en muchos código pero no entendía el porque, buen aporte :).

Una pregunta :confused: , ¿eso se aplica para uno de 32bit? Retomando tu ejemplo, si quiero representar 1200 grados y 2 decimales de precision, necesitaría un valor máximo de 120000, uno de 2 bytes solo almacena 65535, osea no cabe.

Entonces: ¿Solo me queda usar el de 32bit?. Si es así ¿derrochare memoria? ¿Aún si no uso operaciónes en coma flotantes?. Ejm:
Int32 variable = 1250065;
Ahí no use coma pero cuando lo muestre por pantalla el se verá 12500.65 usando lo que tu dices.

Mi gran pregunta es ¿derrochará la memoria en 32bit, aún si no uso coma?.

Otra pregunta: ahorras RAM o ROM.
 
Última edición:
Ahorras de los dos: ROM porque el programa es más corto, y RAM, porque almacenar enteros ocupa menos que los 4 u 8 bytes de los flotantes.

No entiendo lo que quieres decir. ¿Podrías reeditar tu mensaje para explicarlo algo mejor? Me faltan puntos y comas, para poder entender lo que dices. :(

Si, lo que creo que dices, es que con 4 bytes sobra resolución, se pueden usar trucos, como por ejemplo que un uint16t almacene la parte entera, y un uint8 la parte decimal. Pero... el código podría complicarse un poco.
 
Última edición por un moderador:
s controvercial el char y el int

pero hay varios int

int de 8 bits
int 16 bits
int de 32 bits

depende del compilador es el tamaño estandar del int

CCS lo usa como de 8 bits y otros aveces es 32 bits o 16 bits

pero yo por lo regular rara vez ocupo int , uso en su lugar char

asi solo ocupo 8 bits en cualquier compilador siempre me marcara 8 bits

hay que recordar que cuando hacemos un contador de 0 a 10 no importa se sea un simple char
pero si nuestra cuenta es de 0 a 200 debemos usar unsigned char

rara vez ocupo cuentas mayores a 255 normalmente las calculo para que no pasen mas aya de un byte

asi ahorro de 2 a 3 bytes tanto en ROM como en RAM

que no es mucho pero en micros pequeños es evidente, aparte que en rutinas de PWM emulado y SPI emulado es mas rapido trabajar con 1 byte que con 2 bytes

pues es muchisimo mas lento trabajar con 2 bytes juntos, si uno trabaja con 1 byte y quiere uno trabajar con 2 bytes repetimos el algoritmo que sea 2 veces y aun asi sera mucho mas rapido

¿por que?
cuando trabajamos con 1 byte y hacemos el recorrido bit a bit , nuestro algoritmo debe recorrer de 0 a 255

pero si queremos trabajar 2 bytes entonces nuestro recorrido de bit a bit sera de 0 a 65535

¿donde nos es util estas tonterias?

1.- matrices de led
2.- control de PWM emulado "cuando se te acaban los PWM por hardware"
3.-registros de corrimiento "cuando no tienes SPI por hardware"
4.-control de luces led RGB "lo mismo que el punto No 2"
5.-DAC R2R
6.-etc.
 
Me pasó que tuve que armar una matriz de factores que dependían de senos/cosenos, el resultado la mayoría de las veces daba un valor decimal "0,xxxx", si bien no me importaba demasiado el tamaño en código, si tenía que hacer hincapié en la velocidad.

Por tal motivo, decidí usar decimales fijo y en vez de multiplicar por múltiplos de 10, decidí usar desplazamientos a la izquierda, debido a que esos factores que tenía la matriz después se iban a multiplicar por algo, al final de la multiplicación se volvía a desplazar a la derecha (anulando el desplazamiento inicial, pero permitiendo no tener pérdidas durante la multiplicación).
 
Hace muchos años, cuando la tierra aún estaba tibia (1993), tuve que escribir un controlador de posición PID para un motor DC brushless con mando directo que requería un tiempo de muestreo inferior a 1 ms. El micro era un 80C196KB de Intel corriendo a 12Mhz y había que programarlo en PLM (lenguaje de Intel medio parecido al Pascal). La posición la daba un codificador óptico con resolución de 1000 líneas por vuelta que multiplicábamos por 4 con una suerte de filtro digital por hardware para lograr la resolución necesaria en la posición final (así lográbamos cerca de 0.1º).
La unica solución para lograr el tiempo de muestreo necesario y el cálculo de la posición con esa resolución fué usar cálculo en punto fijo, así que tuve que hacer rutinas de multiplicación y división, y unos macros para sumas y restas donde la parte decimal eran 8 bits (1/256) y la parte entera eran 24 bits lo que completaban los 32 bits de un tipo escalar propio del lenguaje que no me acuerdo como se llamaba :oops: :oops:. Por fortuna, el PLM usaba el mismo protocolo del C para las invocaciones a las funciones, así que solo era cosa de ir al stack a buscar los valores y ya.... pero me dió algo de trabajo...no era cuestion de multiplicar por 100 o por 1000...
 
Última edición:
Una pregunta alguien conoce como ahorrar if es que tengo que hacer Muchas comprobaciones y un solo if me ocupa como 4% de la memoria y ando escaso (dentro sólo hay 2 líneas una variable y un return) son como 8 comprobaciones del tipo
If (! (1 && 2 && 3 && 4)) siendo cada número una comprobación
 
Puedes usar una tabla de despacho en función del valor de las expresiones.

O hacer if() en cascada para discriminar los casos más frecuentes.

Necesitaría ver un poco más de código, para aconsejarte mejor.
 
Última edición por un moderador:
Lo colocare cuando llegue a mi casa ahora no se si colocarte el código completo (son como 800 lineas) o solo el pedazo en cuestión son más que todo variables almacenadas del adc del pic
 
Con unas pocas líneas, donde se vea el valor que adquieren las variables, y luego unas pocas líneas donde se vean algunos if(), sería suficiente.
 
Hola te adjunto el archivo con 3 casos donde me gustaria pasarle una optimizadita (ese archivo solo ocupa el 45% del pic no el que te adjunte el completo de 800) y con los demas archivos llega al 90% y todavía me falta por agregar.

Otra cosa dices que es mejor no usar el punto flotante tengo la siguiente formula:
((1 / (2*PI*F))**2) / 0,000000000235

pense en hacerla asi
((1000000000000 / (2*PI*F))**2) / 235

de esa manera no uso flotante lo que pasa es que ese "1" es un numero muy grande y seguro necesita 32 bit asi que no se si vale la pena.

PD: F=frecuencia y va desde 10 hasta 100 mas o menos.
PDD: el "**2" es elevado a la 2.
PDDD: Es un pic 18F4550.
 

Adjuntos

  • archivo.zip
    1.7 KB · Visitas: 10
De la fórmula que has dado, queda reducida a
[LATEX]\frac{5*10^{22}}{47*pi^2*f^2}[/LATEX]​

Si f = 10, el resultado es 1.07787989125528e+18, mientras que
si f = 100, el resultado es 1.07787989125528e+16, con lo que es difícil que se pueda simplificar, ya que no sabemos cómo o con qué escala debe salir al exterior.

Hay que replantearse el problema desde el principio, porque estás manejando escalas muy grandes. No sé si eso es justamente lo que quieres hacer, o si el resultado va a salir en una pantalla tipo LCD o la enviarás por puerto serie a otro equipo... en fin... que faltan detalles para saber el contexto en que estamos trabajando. Por ejemplo, saber de dónde sale el 235, ya que solo podemos deducir que es igual a 47*5 :)

En cuanto al código, se trataría de pasar de
PHP:
if(!((adcv[0] + 100) * 6 >= (sc.res[sc.nRes].volt[0] + 100) * 5 &&
	(sc.res[sc.nRes].volt[0] + 100) * 6 >= (adcv[0] + 100) * 5 &&
	(adcv[1] + 100) * 6 >= (sc.res[sc.nRes].volt[1] + 100) * 5 &&
	(sc.res[sc.nRes].volt[1] + 100) * 6 >= (adcv[1] + 100) * 5)) {
	sc.tmpPartFound = PART_NONE;
	allLow();
	return;
}
a
PHP:
res0  = sc.res[sc.nRes].volt[0] + 100;
res05 = res0 * 5;

res1  = sc.res[sc.nRes].volt[1] + 100;
res15 = res1 * 5;

adc0  = adcv[0] + 100;
adc05 = adc0 * 5;

adc1  = adcv[1] + 100;
adc15 = adc1 * 5;

if (!(adc05 + adc0 >= res05
&&    res05 + res0 >= adc05
&&    adc15 + adc1 >= res15
&&    res15 + res1 >= adc15
)) {
y... si no me he equivocado en la lógica, se podría reducir a:
PHP:
if (adc05 + adc0 < res05
||  res05 + res0 < adc05
||  adc15 + adc1 < res15
||  res15 + res1 < adc15
) {
 
Hola la formula es para un inductometro, el cual usa una compuerta lógica nand con capacitores, de acuerdo a la inductancia de la bobina tardan mas o menos en cargarse si los condensadores estan a full el nand se bloquea (corriente de condensador mas 5v aplicados en la otra patilla), empezando a descargarse cuando se descarga hay corriente en una sola patilla (la de 5 voltios) y empiezan a cargarse produciendo una frecuencia (mientras mayor la inductancia menor la frecuencia).

Con esto se puede medir teóricamente hasta 1000H (probado en proteus) y un mínimo de 0.8H sin perder mucha precisión ese 235 viene dado de la formula:

(C1xC2)/(C1+C2) use dos condensadores de 470nF que me dio ese valor.
(0,00000047 * 0,00000047) / (0,00000047 + 0,00000047) = 0,000000235

Yo le agregue 2 ceros mas porque con F en 240 (disculpa he recordado que una bobina de 1,8H produce una frec de 240) me daría 1,87143 en cambio con esos ceros extra da 187,143 (quería aplicar lo que dijiste) pero luego pensé que si el 235 tiene "," no sirve de mucho.

PD: La resolución basta con 2 decimales con los 2 ceros que explique arriba. (eliminando el 143).
PDD: Ese código que usas quizás no reduzca la ROM me he percatado que si se almacenan así (para aumentar legibilidad) aumenta la ROM.
 
Última edición:
Pues entonces, si no me he equivocado, se puede reducir la fórmula anterior a

[LATEX]\frac{107788.493237}{f^2}[/LATEX]​

y da un resultado con "dos decimales extra", que puedes almacenar ya en un uInt16.

El caso es que no habrá mucha ganancia: la simple presencia de la operación elevar al cuadrado y la posterior división hará que se importe la biblioteca de flotantes.

Una opción a eso, que podría resultar, es lo que en informática solemos hacer cuando hay problemas de rendimiento en la parte de la CPU, y es la cambiar potencia de CPU por memoria RAM. En tu caso, como F varía entre 10 y 240, consistiría en hacer una tabla de 230 valores con el resultado de la operación anterior, y guardarla en el programa. Entonces, ya no necesitarás realizar la operación. Simplemente, en función de F, se obtiene el resultado directo.

Con un sencillo script se puede hacer en un momento:
PHP:
$ perl -E 'say int(107_788.493237 / $_**2) for 10..240'
1077
890
748
637
549
479
421
372
332
298
269
...

En cuanto a la parte de ahorro de ROM en el if(), es cierto: no hay ahorro, pero es porque esas operaciones siempre hay que hacerlas. No puedes obviarlas o deducirlas a partir de otras. Bueno, sí que puedes, pero al final, el número de adiciones y multiplicaciones es el mismo.

¿En cuánto más aumenta la ROM?
 
Última edición por un moderador:
Bueno supongo :cry: tendré que buscar ahorrar memoria en otros lados respecto a la tabla creo que es viable podría hacerlo hasta 255 que me daría un valor mínimo de 1,6H (así podría usar de 8 bits) aunque a mayores inductancia se pierde bastante precisión (aunque no creo que exista algo con mas de 200H si es así que? :eek:. Aunque y que tal si intento esto:
Código:
resultado = (int16) 10778849 / f * f;
Igual se importaría la librería? mi intención (con f=249) es que de 173 (con una funcion que tengo el mostrara en el lcd 1,73 aunque sea un entero).

Una pregunta como hiciste esa reconversión de la formula es muy interesante la simplificaste bastante ya que tengo otras formulas que me gustaría aplicar los mismo y me encantaría ser capaz de hacerlo por mi cuenta :D.

Todavía tengo que hacer una función de medición de voltaje (medirá de dos sitios distinto y corriente es un vatímetro) un medidor de capacitancia que no esta listo el código (son como 250 lineas) y hacer mi pwm (por software) con ciclo ajustable. Lo mas triste es que solo tengo 13% de la memoria del pic (4156 bytes) para implementarlo.:cry:

PD: Gracias por compartir tus conocimientos veo que tienes un foro aparte y que hay fechas incluso menores al 2005 supongo tienes una amplia experiencia en programación, lamentablemente aunque me gusta mucho la pc hace alrededor de 1 año que comencé a programar (también entre a la uni como ing. de información), sin embargo últimamente me ha llamado la atención del mundo de la electrónica.
PDD: Acabo de intentar lo que te dije arriba y ahorre 0,5% de memoria :D (sin el int16 porque sino me da 0). 0,5% es algo :D gracias. a retornar a la guerra de optimizado xd.
PDDD: No se le olvide decirme su secreto de simplificación xd.
 
Última edición:
Debería ser así:
PHP:
resultado = (uint16_t) (10778849L / (f * f));
(los paréntesis son importantes).

Suponiendo que f no llega a 255 se puede declarar como uint8_t; f*f da un resultado que cabe en un uint16_t (entero de dos octetos sin signo); lo que queda es una división entera entre un uint32_t (un long, largo, atención a la 'L' final) y el uInt16. El resultado será otro uint16_t. Y ya tenemos toda una operación con enteros.

En cuanto a la simplificación, solo hay que operar con los números que ya tenemos:
[LATEX]\frac{\frac{1}{(2 \cdot \pi \cdot f)^2}}{235 \cdot 10^{-9}}=\frac{\frac{10^9}{235 \cdot 4 \cdot \pi^2}}{f^2}=\frac{ \frac {10^9}{9277.42813702}}{f^2}=\frac{10778849.3237}{f^2}[/LATEX]​
 
.



.... En cuanto a la simplificación, solo hay que operar con los números que ya tenemos:

[LATEX]\frac{\frac{1}{(2 \cdot \pi \cdot f)^2}}{235 \cdot 10^{-9}}=\frac{\frac{10^9}{235 \cdot 4 \cdot \pi^2}}{f^2}=\frac{ \frac {10^9}{9277.42813702}}{f^2}=\frac{10778849.3237}{f^2}[/LATEX]​

Matemáticas pura y cruda ;).


A no tenerle miedo jamás !!!!.



Saludos, JuanKa.-
 
Otro truco que me han contado hace un par de horas. Depende del compilador, desde luego, pero el gcc-avr a veces tiene problemas para distinguir estos casos.

Supongamos que queremos hacer un bucle de 25 vueltas. Lo escribiríamos así:
PHP:
for (i = 0; i < 25; i++) {
pero, según el compilador -repito-, si lo escribimos de otra manera, podemos ahorrar alguna instrucción:
PHP:
i = 25;
do {
    # ...

} while(--i);
La clave está en la última línea: el compilador la traduce a una instrucción de decremento de la variable, y si el resultado es distinto de cero, repite el bucle, algo que en muchos códigos en ensamblador lo hace en una sola instrucción.
 
es cierto

el do

hace toda las instrucciones 1 vez y las veces que vienen ya son condicionadas

yo lo hacia sin el do

i=0;
while(contador != numero)
{
//codigo
i++;
}

es aveces mas rapido que usar un for , eso lo aprendi cuando hise un jueguito en borland C++ corriendo en una 486

lo habia olvidado y como dices es ddependiendo el compilador el pic al tener pocas instrucciones hace que el mismo codigo en otro CPU sea mas eficiente mientras que en el pic debe estar mas elaborado
 
Última edición:
Atrás
Arriba