Skip to content

Divide by zero in map() function #2219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Gorkde opened this issue Jul 3, 2016 · 5 comments
Closed

Divide by zero in map() function #2219

Gorkde opened this issue Jul 3, 2016 · 5 comments

Comments

@Gorkde
Copy link

Gorkde commented Jul 3, 2016

V 2.3.0
Arduino Uno / ESP-12F

While porting an sketch from Arduino it mysteriously crashed. I isolated the problem to the map() function when the arrays are empty. Once I fill it it works as it should.

The sketch is working without problems on Arduino Uno (returning -1 for each value) but uploaded to the ESP it crashes.

(Background: I need empty calibration data which will be filled later once sensors are read)

Minimal Sketch part to show it:

int FeuchtigkeitRAW[16] = {};    
int FeuchtigkeitMAP[16] = {};     
int FeuchtigkeitCALIB[32] = {};       

void MapPots()        
{
  for (byte i = 0; i < 16; i++)
  {
    FeuchtigkeitMAP[i] = map(FeuchtigkeitRAW[i], FeuchtigkeitCALIB[i], FeuchtigkeitCALIB[i + 16], 0, 100);
  }
}

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

void loop()
{
  MapPots();                             
  Serial.print("Wert ");
  for (byte i = 0; i < 16; i++)
  {
    Serial.print(FeuchtigkeitMAP[i]);
    Serial.print(" ");
  }
  Serial.print("\n");
  delay(1000);
}
@igrr igrr added this to the 2.4.0 milestone Jul 4, 2016
@krzychb
Copy link
Contributor

krzychb commented Jul 4, 2016

I like this one as I can ask about exception cause 6 - IntegerDivideByZeroCause 😄

Adruino map() function is documented here including the formula.
I see implementation of identical formula for ESP here.

Passing non initialized int arguments to this function means effectively passing zero’s: map(0, 0, 0, 0, 100). Having such arguments will result in attempting to divide by zero 💥

Handling of division by zero for integer numbers on various Arduino boards can be quickly checked with a short sketch:

void setup()
{
  Serial.begin(115200);

  int a, b;
  a = 0;
  b = 1 / a;

  Serial.print("Result = ");
  Serial.println(b);
}

void loop() {}

Results are as follows (Arduino IDE 1.6.9):

Arduino UNO (core ver. 1.5.11):
arduinouno-1

Arduino DUE / SAM (core ver.1.6.7) :
arduinodue-1

ESP8266 / Arduino (core ver. 2.3.0):
nodemcu_1 0

This explains why map(0, 0, 0, 0, 100) is working on Arduino UNO and crashing on ESP.

Personally I prefer to keep the ESP telling me that I am performing an illegal operation instead of potentially seeping this fact under carpet by returning -1 or 0 as a result. Having legitimate -1 inside some complex calculations may return seemingly legitimate result for invalid arguments.
Therefore such bug may stay in code unnoticed forever, while ESP will flag it immediately, before the code is even released.

Now, returning to my original question, does anybody know why division by zero is flagged with EXCCAUSE 0 - IllegalInstructionCause? Basing on the Exception Causes table I would expect 6 - IntegerDivideByZeroCause instead.

@Gorkde
Copy link
Author

Gorkde commented Jul 4, 2016

I assumed the same (divide by zero) but since -1 is in general "Error" you can check for it but crashing is no option that's acceptable for my opinion.

@igrr
Copy link
Member

igrr commented Jul 5, 2016

@krzychb It's slightly weird that you get an exception at PC=0x40106889. When I run the same sketch exception happens, as I would expect, at 0x4000dce5. Note 0x4000 part — this is in ROM, where _divsi3 (software integer division, 32-bit arguments) function is located. I am running git version though, maybe something is wrong in 2.3.0 (although the end result is the same).

Exception (0):
epc1=0x4000dce5 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000

ctx: cont 
sp: 3ffef610 end: 3ffef7e0 offset: 01a0

>>>stack>>>
3ffef7b0:  3fffdad0 00000000 3ffee794 40201c44  
3ffef7c0:  feefeffe 00000000 3ffee7b0 4020211c  
3ffef7d0:  feefeffe feefeffe 3ffee7c0 40100108  
<<<stack<<<

Regarding your question why we get exception 0 and not exception 6, it's pretty easy. CPU core inside the ESP8266 doesn't have hardware division instruction enabled. If it did, this instruction would cause exception 6. Because there is no division instruction, gcc calls aforementioned __divsi3 function each time it encounters division in program text. Looking at the source of this function, you'll see that authors decided to use ill, an illegal instruction opcode, to indicate division by zero.
https://github.com/jcmvbkbc/gcc-xtensa/blob/call0-4.8.2/libgcc/config/xtensa/lib1funcs.S#L634-L639

Overall, i don't consider the fact that integer division triggers exception an issue. Just as unaligned reads/writes work (albeit slowly) on some architectures and don't work on others. However, I think we should still check divisor against zero in map function.

@igrr igrr changed the title Crash by map() while working on Arduino Uno Divide by zero in map() function Jul 5, 2016
@drmpf
Copy link

drmpf commented Jul 5, 2016

I have just implemented this function in my code generator app (pfodDesignerV2). In that case I use the code like the following
long div = (in_max - in_min);
if (div == 0) div = 1;
long scale = (out_max - out_min) / div ; // do this first to avoid avoidable overflows
// from (x - in_min) * (out_max - out_min)
return (x - in_min) * scale + out_min;

@me-no-dev
Copy link
Collaborator

"fixed" to return -1 in case of division by zero

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants