Divide by zero / variable typing issue

This forum is for all topics related to Simulator for Arduino v0.98. Please include sample code where possible.

Moderator: Adrian

joshuabardwell
Posts: 3
Joined: Tue Aug 06, 2013 1:08 pm

Divide by zero / variable typing issue

Postby joshuabardwell » Tue Aug 06, 2013 1:15 pm

I am having an issue with my code. Here's a code snippet that illustrates the issue:

Code: Select all

long arcMillis;
long windowMillis;

// these lines always produce 0 in workCycle
// float workCycle = arcMillis / windowMillis;
// float workCycle = (float)arcMillis / (float)windowMillis;

// but this works...
float x = arcMillis;
float y = windowMillis;
float workCycle = x / y;


Folks on another forum have indicated that the second "always produce zero" line, at least, should work, but it doesn't.

Here's another example of a similar problem:

Code: Select all

int arcMinutes = 0;
long arcMillis = 0;

// this line produces divide by zero error
arcMinutes = arcMillis / 60 / 1000;


It's unclear to me why that line is producing divide by zero. I realize that I would be better to typecast the operators as floats, but I think the operation should still be valid on ints and longs. I don't see where divide by zero is coming from. Another person has confirmed that the above line produces the expected result (zero) on an actual Arduino board.

If it's helpful, here is my entire code so far, including the workarounds I have come up with to try to address this issue.

Code: Select all

const int greenLED = 13; // green LED's output number
const int redLED = 12; // red LED's output number
const int windowMinutes = 10; // number of minutes in the duty cycle "window"
const int loopDelay = 250; // ms delay between loops

// Variables related to time-keeping

long previousMillis; // milliseconds at previous loop
long triggerMillis = -1; // time at which arc first triggered window to start, -1 means no trigger
long arcMillis = 0; // number of milliseconds that the arc has been active within this window
int windowExpireMinutes = windowMinutes; // time until current window expires
int arcMinutes = 0; // minutes of arc active during this window

// Variables related to detecting the arc

int sensorRead; // holds readings from analog sensors
float arcVolts; // voltage from the welding leads
int welderOCV = 5; // nominal OCV of the welder, relative to 5 volts
float arcOCVpercent = 0.90; // percent of OCV that indicates arc is active

// Variables related to calculating the duty cycle

long windowMillis = windowMinutes * 60 * 1000;  // number of milliseconds in the duty cycle "window"
float dutyCycle = 0.01; // duty cycle of the welder

void setup() {
  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  digitalWrite(redLED, LOW);
  digitalWrite(greenLED, HIGH);

  // this will immediately get overwritten at the first loop, but we have to set it to something
  previousMillis = millis();
}

// Current method of detecting presence of arc is to measure voltage between welding leads.
// OCV (no arc) will be higher than arc voltage. This method may prove to be less than ideal,
// in which case some other method will be implemented.

boolean checkArc() {
  sensorRead = analogRead(A0);   // read input from analog pin A0
  arcVolts = sensorRead * (5.0 / 1023.0); // convert sensor read to volts on 5 volt scale
  return arcVolts < (welderOCV * arcOCVpercent);
}

void loop() {
  long currentMillis = millis(); // note the time at the beginning of the loop, for consistent reference

  // stuff to do only if the arc is active during this loop
  if (checkArc()) {
    if (triggerMillis < 0) { // if this is the first time since window expiration that the arc has triggered the window
      triggerMillis = currentMillis; // note the time the window was triggered
    }
    else { // we are in the middle of a window and the arc is active
      arcMillis += currentMillis - previousMillis; // add to the cumulative arc time

      // no idea why float workCycle = (float)arcMillis / (float)windowMillis always produces 0

      float x = arcMillis;
      float y = windowMillis;
      float workCycle = x / y;

      // What to do when duty cycle has been exceeded

      if (workCycle > dutyCycle) {

        // set LEDs. This will be redundant for most loops, but I bet it's not a worse
        // penalty than implementing logic to only do it the first time

          digitalWrite(redLED, HIGH); // turn on the red LED -- stop welding!
        digitalWrite(greenLED, LOW); // turn off the green LED

        // If the arc continues to be active after duty cycle has been exceeded
        // then add the additional arc-active time to the current window. For example,
        // if the welder has a 20% duty cycle (2 minutes), but the operator welds for
        // 3 minutes, the total window time will be extended to 11 minutes, rather than
        // 10. This is implemented by simply adding one loopDelay to the trigger time
        // for each loop during which the arc is active after duty cycle has been
        // exceeded.

        triggerMillis += loopDelay;
      }
    }
  }

  if ((currentMillis - triggerMillis) > windowMillis) { // if window has expired
    triggerMillis = -1; // reset the triggered state
    arcMillis = 0; // reset the cumulative arc time   
    digitalWrite(redLED, LOW); // turn off the red LED (if it was off, no change)
    digitalWrite(greenLED, HIGH); // turn on the green LED -- good to weld!
  }

  // check status of "reset" button

  // check status of "calibrate" button -- assumes OCV method is used to detect arc

  // check status of "duty cycle" pot

  arcMinutes = (float)arcMillis / 60.0 / 1000.0;
  windowExpireMinutes = (currentMillis - triggerMillis) / 60.0 / 1000.0;

  delay(loopDelay); // delay -- must make sure each loop is > 1 ms at least to allow correct accumulation of arcMillis
  previousMillis = currentMillis; // set previousMillis for the next run of the loop
}

joshuabardwell
Posts: 3
Joined: Tue Aug 06, 2013 1:08 pm

Re: Divide by zero / variable typing issue

Postby joshuabardwell » Fri Aug 09, 2013 12:24 pm

No replies? Have I done something wrong? What is the preferred way to get support for this program?

Simulator_admin
Site Admin
Posts: 239
Joined: Thu Feb 02, 2012 6:07 pm

Re: Divide by zero / variable typing issue

Postby Simulator_admin » Sat Aug 10, 2013 7:14 pm

Hi,

Sorry for the delay. We did have a forum admin who was very good and diligent but looks like he has moved onto bigger and better things.

With this line below, the Simulator determines the operators from left to right:

Code: Select all

arcMinutes = arcMillis / 60 / 1000;


So, the Simulator will find the first / divide operator and split the line into arcMillis divide by 60/1000. Since 60/1000 is an expression, the Simulator will divide 60 by 1000 and return 0. So the right side of the equals sign is now arcMillis divide by 0 which gives the divide by zero error.

One way to fix this is with brackets:

Code: Select all

arcMinutes = (arcMillis / 60) / 1000;


With the real Arduino board, it would be interesting to find out if the Arduino returns the correct value or if it hides the divide by zero error and just returns zero. The Simulator is supposed to mimic the exact operation of the Arduino board so if there is a difference, this can be modified.

With the first two examples, definitely the second example should work. Looking into this, this is a very deep bug, but we have found a fix which will be out in the next version. Thanks for picking this up.

joshuabardwell
Posts: 3
Joined: Tue Aug 06, 2013 1:08 pm

Re: Divide by zero / variable typing issue

Postby joshuabardwell » Sun Aug 11, 2013 3:41 am

So, the Simulator will find the first / divide operator and split the line into arcMillis divide by 60/1000. Since 60/1000 is an expression, the Simulator will divide 60 by 1000 and return 0. So the right side of the equals sign is now arcMillis divide by 0 which gives the divide by zero error.


I think that equal operators are supposed to be evaluated from left to right. E.g.: 100 / 20 / 5 is implicitly (100 / 20) / 5. Both MS Excel and my Arduino agree. Arduino produces an output of 1 for the following code:

Code: Select all

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int x = 100 / 20 / 5;
  Serial.println(x);
}


The mechanism you seem to be suggesting, if I understand correctly, would produce an output of 25.

Simulator_admin
Site Admin
Posts: 239
Joined: Thu Feb 02, 2012 6:07 pm

Re: Divide by zero / variable typing issue

Postby Simulator_admin » Sun Aug 11, 2013 2:47 pm

Seems like you know your stuff. We had a look into this and to make this work, the operators will have to be evaluated from right to left. In the code of the Simulator, this was how it worked a few versions ago but it was changed to work from left to right.

Anyway, we have changed this so that it now works as suggested 100/20/5 will now produce 1 as suggested in line with a real Arduino. thanks for pointing this out and chasing up to get an answer. We are lucky you are on the ball. The next version is due for release at the end of the month.

By the way, with the automated testing, we noticed that the only example sketch this affected was the 6.Sensors/Ping.ino sketch which had the following routines:

Code: Select all

long microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;
}


The automated testing outputs have been changed to reflect these changed answers. Thanks again.


Return to “Version 0.98”

Who is online

Users browsing this forum: No registered users and 1 guest