Esse Bug está me deixando Louco, Salvando FLOAT na EEPROM

Pessoal gostaria de entender porque ocorre esse BUG ao salvar essa variável float na EEPROM.

Sei que só posso salvar um Byte por vez em cada posição da EEPROM, por isso separei a variável em duas partes, salvando em dois endereços. O código estava funcionando, até achar um número que ele BUGA.

Quando tento salvar 8.49 ele sempre retorna 8.48.
Mas o mesmo NÃO ocorre com 8.59, que nesse caso retorna certo (8.59).

Já identifiquei que o erro acontece aqui:

(byte((coef-byte(coef))*100))

Mas não entendi porque funciona com praticamente todos os outros números decimais, menos com .49

Segue abaixo o código de teste com saída de depuração para a serial:

#include <EEPROM.h> // incluir a biblioteca

const byte addr3 = 14;
const byte addr4 = 18;
float coef = 8.49;

void setup() {

Serial.begin(9600);

}

void loop() {


  delay(5500);
  Serial.println("Imprimindo Variavel FLOAT 8.49");
  Serial.println(coef);
  delay(5500);

  Serial.println("Gravando EEprom ADDR3 e ADDR4");  
  EEPROM.write (addr3,byte(coef)); 
  EEPROM.write (addr4,byte((coef-byte(coef))*100));
  
  delay(500);
  
  Serial.println("imprimindo na serial: (byte((coef-byte(coef))*100))");
  Serial.println(byte((coef-byte(coef))*100));
  
  delay(500);

  Serial.println("Leitura EEprom ADDR3");    
  Serial.println(EEPROM.read(addr3));
  Serial.println("Leitura EEprom ADDR3");      
  Serial.println(EEPROM.read(addr4));

  delay(4500);

  Serial.println("Leitura EEprom ADDR3+ADDR4");      
  float coef = float(EEPROM.read(addr3))+ float(EEPROM.read(addr4))/100; // = 8.49;
  Serial.println(coef);

  delay(5500);

  Serial.println("Setando variavel FLOAT coef = 8.49") ;     
  coef = 8.49;
  Serial.println("Imprimindo variavel FLOAT coef = 8.49");      
  Serial.println(coef);
  Serial.println("Loop");      
  delay(5500);


}

Obrigado pela força.

Olá @maikoherrmann82 ,
apesar de uma variável float ocupar 4 bytes na memoria, ao salva-la na EEPROM
não podemos salva-la e recupera-la byte a byte, o valor recuperado não representará um valor float.
Experimente usar EEPROM.put(address, data) e EEPROM.get(address, data) ao inves de
EEPROM.write(address, data) e EEPROM.read(address, data).
Ao gravar varias posições de EEPROM, o endereço deve ser incrementado de 4 em 4 bytes.

Rode este código:

#include <EEPROM.h> // incluir a biblioteca

const byte addr3 = 14;
float coef = 8.49;
float coef2 = 0;
void setup() {

  Serial.begin(9600);
}

void loop() {

  delay(500);
  Serial.println("Imprimindo Variavel coef");
  Serial.println(coef);
  delay(500);

  Serial.println("Gravando EEprom ADDR3");
  EEPROM.put (addr3, coef);

  delay(500);

  EEPROM.get(addr3,coef2);
  Serial.println(coef2);

  delay(5500);

}

RV mineirin

1 Like

Prezado amigo, muito obrigado pela pronta-resposta.
Realmente com o PUT e GET funcionam perfeitamente, questão é que não consigo entender porque não funciona aquele tratamento da variavél:

Serial.println(byte((coef-byte(coef))*100)); // (8.49 - 8) * 100 = 49 

(8.49 - 8) * 100 = 49
porém retorna 48

Com 8.59:
(8.59 - 8) * 100 = 59
Ele retorna certo: 59

Pois na realidade o problema não está na gravação na eeprom e sim nessa fórmula, eu só preciso entender o porque, pois coisas sem explicação me tiram o sono, kkkk.
Porque funciona com 8.59 e com 8.49 não? Isso que me deixa louco.

Eu só precisava de uma explicação lógica para poder descansar em paz, na teoria deveria funcionar e até funciona. Questão está em determinados números impares, mas não é em todos, como mencionei 8.59 retorna certo 59.

Obrigado pela atenção!

Ola @maikoherrmann82
um numero float no arduino (atmegaxxxx) tem um aproximação de 6 (7) dígitos decimais.
Quando você imprime usando :
Serial.println(coef); somente são impressos 2 dígitos decimais.
para ver todos 7 dígitos decimais use: Serial.println(coef,7);
Números de ponto flutuante não são exatos, e podem gerar desvios.

No seu código original, mesmo antes de gravar na EEPROM o numero já era 48.
Fiz um resumo bem pequeno do seu código para que voce veja o numero antes mesmo de gravar na EEPROM.
8.49 aparece com 8.4899997e 8,59 aparece como 8.5900001

float coef = 8.49;
float coef2 = 8.59;
//-------------------------------------------------------
void setup() {
  Serial.begin(9600);
}
//-------------------------------------------------------
void loop() {

  delay(1500);
  Serial.println(coef,7);
  Serial.println(byte((coef-byte(coef))*100));
  Serial.println(coef2,7);
  Serial.println(byte((coef2-byte(coef2))*100));
}
8.4899997
48
8.5900001
59
8.4899997
48
8.5900001
59

1 Like

Muito Obrigado por esclarecer minha dúvida.
Isso me traz um grande alívio, de fato já havia lido a muito tempo sobre a não exatidão e resultados errados de cálculos com valores flutuantes, mas não imaginei que seria nesse nível. Não tinha pensado em imprimir todos os número decimais, isso teria me poupado noites de sono, kkk.

Irei observar com mais cuidados daqui pra frente.

Teria alguma dica pra no futuro fazer esse tipo de correção? Tem como usar o ROUND nas casa decimais?

Obrigado!

Isto não é verdade... Basta ver o código da biblioteca EEPROM para ver que a mesma guarda o valor do float byte a byte. O mesmo é simples de fazer utilizando uniões.

union Float_8 {
   float float_value; 
   unsigned char array_value[4]; 
};

//... 

union Float_8 dado_EEPROM, lido_EEPROM; 
dado_EEPROM.float_value = 3.14; 

for (unsigned char i = 0; i < 4 ; i++) {
   EEPROM.write(10+i, dado_EEPROM.array_value[i]); 
}; 


//... 

//ler da EEPROM
for (unsigned char i = 0; i < 4 ; i++) {
   EEPROM.read(10+i, lido_EEPROM.array_value[i]); 
}; 

Serial.println(lido_EEPROM.float_value); 

Sim, não é directo e a put e get faz o mesmo com passos possíveis em C++, mas o mecanismo é o mesmo. Nota que é também possível de fazer exactamente o mesmo sem uniões e com bit shift mais máscaras.

Serve apenas para elucidar quem levantou a questão original que existe mais mundo além do Arduino.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.