Go Down

Topic: How to avoid floating point math (Read 3172 times) previous topic - next topic

cornwallav8r

I have now 2 weeks solid in programming a new project, and as a newbie I don't mind figuring out my screwups on my own, but now I have what appears to be stack overflow issues due to the overhead of multiple timing interrupts and sensor pulse input interrupts overloading the processor....so my question is....I need to eliminate the floating point math I threw in....I can't remember the tips I read somewhere about how to avoid floating point where workarounds can be done....fractions in my case will kill the project, what is one to do...

Are you to multiply the parts by say, 100 or 1,000 before multiplying or dividing, to move the fractional part up, then afterwards divide it back down, thus preserving the initial decimal fractional part?  Is there more to it?
Thanks as always...

PaulS

Quote
what is one to do

When posting in the Programming forum it's generally advisable to POST SOME CODE!

Graynomad

I think the easiest way is to work with large integer numbers with small granularity.

For example you need volts to 3 decimal places. Do all the work in mV and drop a decimal point in the right place when displaying data to a human.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

cornwallav8r


   The whole code is too large but here are the 3 interrupt routines and associated calcs needing revision...basically I am calculating flow rate and its average going forward

Code: [Select]
#include <LiquidCrystal.h>
#include <Time.h> //added to do one second   timer
#include <TimeAlarms.h> //added to do one second   timer
#include <EEPROM.h>




void setup() {  
needed for 1 sec timer
 
 Alarm.timerRepeat(5,Repeats);    // timer for every (5) seconds  calls Repeats subroutine- upped to 5 due to low counts in a few seconds  
 Alarm.timerRepeat(3,Toggle);    //switch displays while operating
 Alarm.timerRepeat(60,ElapsedTime);    //increment operating time by one minute while operating
 
 
 state=0;
 lcd.clear();
 lcd.begin(16, 2); //rows x cols
 lcd.setCursor(0,0);
 lcd.print("Fuel Flow Gauge");
 
 

 
 
 Serial.begin(9600);

 k_factor = eepromReadInt(9);  
 delay(5);

 lcd.setCursor(0,1);
  lcd.print("K-factor is ");
 lcd.print(k_factor);  
 Serial.print("K-factor is ");
 Serial.println(k_factor);
 delay(1000);

 
   hobbs_hours = eepromReadInt(6);  //reads 2 bytes of integer hours
   delay(5);
   hobbs_minutes = EEPROM.read(8);   //reads stored minutes..
   delay(5);
      lcd.clear();
      lcd.print("Hobbs=   ");
      lcd.print(hobbs_hours,2);
      lcd.print(":");
      lcd.print(hobbs_minutes,2);          
                 Serial.print("Hobbs stored time is ");
                 Serial.print(hobbs_hours);
                  Serial.print(":");
                  Serial.println(hobbs_minutes);

for (int thisReading = 0; thisReading < numReadings; thisReading++)
  readings[thisReading] = 0;  
 delay(1000);
 pinMode(hallsensor, INPUT); //initializes DIGITAL pin 2 as an input
 attachInterrupt(0, IncrementFlowPulse, RISING); //and the interrupt is attached
 
 sei(); //go ahead and start counting
}



void IncrementFlowPulse ()    
{
       
  total_pulses++ ;  
     Count++;
     tenth_gallon++;  
   running=1;
 }  



void Repeats(){

 
   
    Calc = ((float)Count* ((float)720.0 / (float)k_factor)); //(Counts in 5 seconds/ number counts per gallon, converted from seconds to hours
                         //using a large value we can select any k-factor - use counts per gallon - perfect
                         // Calc is the gallons per hour.
                         
    if (!flag){  //reset flag so running total flag is run only once
   flag=1;}      //this is used for averaging routine for average flow rate

 if (Count>0){
    stopped_flag++; //add 5 seconds to running time length
   
    }
   if (stopped_flag>5){  
                           
      stopped_flag=5;
                     
         }
   
 if (Count==0){
    stopped_flag--;
 
     }
     if (stopped_flag<=0){
      stopped_flag=0;  // no longer running
        }
       else { running=1;  
        }
       
 Count=0;  //reset for next interrupt
 
               
   if ((stopped_flag<=0) && (running) ){
 
   state=10;  
   return;
   }
   
 
   
   
}

void Toggle(){
 
 if (state==0){
   state=1;
   return;
 }
 if (state==1){
   state=2;
   return;
 }
   if (state==2){
   state=0;
   return;
 }
}  

void ElapsedTime(){
 if ((stopped_flag>0) && (running)){    
    Elapsed_min++;    
   }
  if (Elapsed_min>59){
  Elapsed_min=0;
  Elapsed_hour++;
  return; }
 }


void loop ()  
{      
  Alarm.delay(1);


 if (flag){  
   
   
     total= total - readings[index];        
      
     readings[index] = Calc;
   
     total= total + readings[index];      
   
     index++;                    

 
     if (index >= numReadings)              
 
       index = 0;                          

   

     average = total / (float)numReadings;        
   
     delay(1);      
 }
 
                         
 if ((tenth_gallon) >=(k_factor/10)){  
     decrement_fuel_level();  
                       
         tenth_gallon=0;    // reset it fro another count
     }
       
    switch (state) {  
    case (0):      
       state0();    
       break;
     case (1):
       state1();  
       break;
     case (2):
       state2();  
       break;
   case (6):
       state6();  
       break;
   case (7):
       state7();  
       break;
    case (8):
       state8();  
       break;
    case (9):
       state9();  
       break;
    case (10):  
       state10();  
       break;
   default:  
       break;
          }  
}
        // end of loop  

void state0()  
 {
   lcd.clear();
   lcd.noBlink();  
   lcd.setCursor(0,0);
   lcd.leftToRight();
   lcd.print (Calc, 2);
   lcd.leftToRight();
   lcd.setCursor(5,0);
   lcd.print ("gal/hr inst");
     if (total_pulses ==0){
       average=0;
   }
   
   lcd.setCursor(5,1);
   lcd.print ("gal/hr avg");
   lcd.setCursor(0,1);
   lcd.leftToRight();
   lcd.print (average, 2);
   
   

 check_keypad();  //see if state has changed via select button
  if (key != -1) {  //only change value once, don't reset it
      a=key;  
  }
   delay(60);  
  switch (a) {
   case 4:
       state=6;  //go to first menu ...
       break;
   default:  //else do nothing
       break;
       }      

 
   
}

Coding Badly


[font=Courier New]void setup() { 
  ...
  sei(); // <-- THIS SERVES NO USEFUL PURPOSE.  REMOVE IT.
  ...
}[/font]

PaulS

Code: [Select]
(float)720.0
Why are you casting a float to a float?

Code: [Select]
     if (!flag){  //reset flag so running total flag is run only once
    flag=1;}      //this is used for averaging routine for average flow rate

Make up your mind. The variable flag is either a boolean or it isn't. If it is, use true or false when assigning it a value. If is isn't, don't treat it like one in an if test.

Code: [Select]
  if (Count>0){
     stopped_flag++; //add 5 seconds to running time length
     
     }

How does incrementing stopped_flag add 5 seconds to running time length?

Code: [Select]
  if (Count==0){
     stopped_flag--;
   
      }
      if (stopped_flag<=0){
       stopped_flag=0;  // no longer running
         }
        else { running=1; 
         }

Has anyone ever told you that your indenting is atrocious? Well, now they have. The Tools + Auto Format menu item sorely needs to be executed.

Code: [Select]
void Toggle(){
 
  if (state==0){
    state=1;
    return;
  }
  if (state==1){
    state=2;
    return;
  }
    if (state==2){
    state=0;
    return;
  }

would certainly be a lot shorter as
Code: [Select]
void Toggle()
{
  state++;
  if(state == 2)
    state=0;
}


Code: [Select]
void loop ()   
{     
   Alarm.delay(1);


  if (flag){ 
     
     
      total= total - readings[index];       
       
      readings[index] = Calc;
     
      total= total + readings[index];     
   
      index++;                   

 
      if (index >= numReadings)             
   
        index = 0;                         

     

      average = total / (float)numReadings;       
     
      delay(1);       
  }
 
                           
  if ((tenth_gallon) >=(k_factor/10)){ 
      decrement_fuel_level(); 
                         
          tenth_gallon=0;    // reset it fro another count
      }
       
     switch (state) { 
     case (0):       
        state0();   
        break;
      case (1):
        state1();   
        break;
      case (2):
        state2();   


Blank lines galore, and then suddenly everything's jammed together. Is there a reason for that?

Personally, what I see here is a lot of slow writing to a LCD, and yet you want to do away with a few floating point operations in the hopes of speeding up the program. I think you're looking in the wrong place for bottlenecks to deal with.

cornwallav8r

Thanks for the input, but please keep the insults to a minimum. People come here to learn and get tips and help not passive aggressive putdowns.  The formatting got trashed when I pasted it in here...my original coding is 1300 lines and is indented reasonably well, though maybe not to your standards.  The pasted mess above looks formatted completely different.  Though I have of course a lot to learn, and appreciate the feedback.    I will pick up the fixes above and keep truckin'.  Though I think what is happening, is when the counts get high due to high flow,  stack overflow occurs, and the thing crashes.  So I suspect there are still problems....

regarding float of a float, I have been trying all kinds of things to get math to work, didn't catch that the number wasn't converted back to integer.. newbies do that... :-)

Coding Badly

Quote
Are you to multiply the parts by say, 100 or 1,000 before multiplying or dividing, to move the fractional part up, then afterwards divide it back down, thus preserving the initial decimal fractional part?  Is there more to it?


Roughly, sort of.  Powers-of-2 are also common.  There are minor advantages and disadvantages to each.

Quote
but now I have what appears to be stack overflow issues


Unless you are trying to run your program on an ATtiny13 processor I don't see it.

Quote
due to the overhead of multiple timing interrupts and sensor pulse input interrupts overloading the processor....


Huh?


Take a step back.  Take a deep breath.  Describe the symptom(s) rather than what you believe to be the cause.

cornwallav8r

Auto format. That's pretty cool.  Is there a users' manual for this simple IDE somewhere out there? i haven't seen one.  Lots to learn.

cornwallav8r

The reason I suspect stack overflow....there are plenty of places in the code for me to learn and fix things, but it basically works.
When I blow into the flow sensor for testing, I get real world reasonable flow rates, (1-12 gallon per hour) and see the fuel level indicator decrement properly, but if I blow a bit harder, which would simulate when an engine is first started up and fills the fuel line, the rate will jump really high....when I do that, I get trashed values, even the fuel quantity value, which is incremented downward linearly with flow, goes to some unknown silly value, I think it is 655 replacing the unit and tenths digits. Which is why I think the interrupts can't keep up... 

Nick Gammon

Code: [Select]
    case (0):       
    state0();   
    break;


The brackets around the zero are not necessary, and are confusing.




Quote
now I have what appears to be stack overflow issues due to the overhead of multiple timing interrupts and sensor pulse input interrupts overloading the processor


I only see one interrupt:

Code: [Select]

  attachInterrupt(0, IncrementFlowPulse, RISING); //and the interrupt is attached


And it doesn't do much:

Code: [Select]

void IncrementFlowPulse ()     
{
  total_pulses++ ; 
  Count++;
  tenth_gallon++;   
  running=1;




Although without seeing the data types it is hard to be sure.

Quote
I need to eliminate the floating point math I threw in ...


This is guesswork, really. The code you've shown in an interrupt would hardly slow it down that much.

I'm not sure what you mean by "stack overflow issues".

Possibly you are simply writing off the end of an array. Hard to say without seeing all the code.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

cornwallav8r

#11
Nov 30, 2012, 07:39 am Last Edit: Nov 30, 2012, 07:57 am by cornwallav8r Reason: 1
3 interrupts...correct, the first and third are pretty simple. The middle one though, has floating point calcs

Code: [Select]

void IncrementFlowPulse ()     //This is the function that the flow sensor interrupt on pin 2 calls to add to the count and total count
{
 //This function measures the rising and falling edge of the hall effect sensors signal
 total_pulses++ ;  //change to this variable so running tally is incremented in the background...
 Count++;
 tenth_gallon++;   //be careful here, fuel_tenth and plural exist. this is used to decrement by tenth gallon once it is high enough
 running=1;
}  

// The setup()  runs once, when the sketch starts
// the FOLLOWING loop() method runs over and over again,
// as long as the Arduino has power
//****************************************************************************************    

void Repeats(){ //interrupt timer1 repeats every second-changed to 5 seconds...

 //here is the math...we sample every 5 seconds.  9200 counts per gallon. FIXED-use float cast to convert integers to float before calculating

 Calc = ((float)Count* ((float)720.0 / (float)k_factor)); //(Counts in 5 seconds/ number counts per gallon, converted from seconds to hours
 //using a large value we can select any k-factor - use counts per gallon - perfect
 // Calc is the gallons per hour.

 if (!flag){  //reset flag so running total flag is run only once
   flag=1;
 }      //this is used for averaging routine for average flow rate

 if (Count>0){
   stopped_flag++; //add 5 seconds to running time length

 }
 if (stopped_flag>5){  //this is to delay the time till we stop and wait....
   //30 seconds or so...it decrements with no flow
   stopped_flag=5; //add 5 seconds to length of time running, up to 30 seconds
   //-each unit of increase = 5 seconds of stoppage
 }

 if (Count==0){  //decrement once per 5 second with zero flow...
   stopped_flag--;

 }
 if (stopped_flag<=0){
   stopped_flag=0;  // no longer running
 }
 else {
   running=1;  //added this to ensure it stays running with flow
 }

 Count=0;  //reset for next interrupt

 //For some reason, when we start back up from stop, we re-enter the save routine.
 if ((stopped_flag<=0) && (running) ){  //we were running, then stopped

   state=10;  //keeps returning to state 10, stop and save, and looping again
   return;
 }




}
//****************************************************************************************

void Toggle(){ //this is an interrupt to switch screens every few seconds

 if (state==0){
   state=1;
   return;
 }
 if (state==1){
   state=2;
   return;
 }
 if (state==2){
   state=0;
   return;
 }
}  

Nick Gammon

Code tags? Makes it easier to read.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

cornwallav8r

I sorta wish I had dived into the forum last week, one can learn a lot in a big hurry with such awesome quick feedback.
I just don't want to be any more hassle than one has to be, don't want to be that noob who thinks somebody needs to implant knowledge in his brain without indulging first and learning some the hard way...

cornwallav8r

#14
Nov 30, 2012, 07:47 am Last Edit: Nov 30, 2012, 07:58 am by cornwallav8r Reason: 1
ahem...what's a code tag?
Ok got it. Revised. Thanks

Go Up