Not expected behavior in mathematical action

hello, I'm testing an arduino nano with PWM output 500-10 hz
arduino have incorrect behavior, a mathematical action in line 62 (int duty = (frq*10)/inp;), why when the <19 hz frequency variable "duty" is reached in a negative value?

// Set-up fast PWM on the Arduino UNO at 1Hz on Digital pin D9

// arduino.cc thread: http://forum.arduino.cc/index.php?topic=582176.new;topicseen#new
// https://forum.arduino.cc/t/inverting-pwm-signal-with-direct-port-addressing/461940/5
// https://docs.arduino.cc/tutorials/generic/secrets-of-arduino-pwm
// https://www.arduino.cc/reference/en/language/functions/math/map/

// veikia!!
const int in1Pin = 7;      // select the pin for the in state
const int in2Pin = 8;      // select the pin for the in state

int instate1 = 0;
int instate2 = 0;



void setup() {
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  pinMode(10, OUTPUT);                               // Set digital pin 9 (D9) to an output
  pinMode(in1Pin, INPUT);
  pinMode(in2Pin, INPUT);
                                                    // +5--/\/\/- 330Ω -->|--DIO9


  TCCR1A = _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(WGM11); 
  
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  byte tccr1a,tccr1b;
  ICR1 = 118;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 100;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249
  OCR1B = 100;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249

Serial.begin(9600);
Serial.print("TCCR1A - ");
Serial.println(TCCR1A,BIN);
Serial.print("TCCR1B - ");
Serial.print(TCCR1B,BIN);
}

void loop() {
 // inA = _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
  //inB = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);                // Enable the invert PWM output OC1B on digital pins 10

  instate1 = digitalRead(in1Pin);
  instate2 = digitalRead(in2Pin);

  if (instate1 == HIGH)  {
    TCCR1A =  _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(WGM11);           // Enable the PWM output 9, invert PWM output 10

    }


  if (instate2 == HIGH)  {
    TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);            // Enable the PWM output 10, invert PWM output 9

    }
    
  int pot0 = analogRead(A0);
  int pot1 = analogRead(A1);
  int frq = map(pot0, 0, 1023, 6249, 118); //(pot0, 0, 1023, 3124, 118)
  int inp = map(pot1, 0, 1023, 100, 19);
  int duty = (frq*10)/inp;
  
  ICR1 = frq;
  OCR1A = duty;
  OCR1B = duty;
  Serial.print("FRQ - ");
  Serial.println(frq);
  Serial.print("INP - ");
  Serial.println(inp);
  Serial.print("DUTY - ");
  Serial.println(duty);

  delay(100);

}

Because you have not said otherwise I am going to assume that you are using an Arduino board that uses an AVR processor. If that is not the case, then please tell us which board you are using

Firstly, you are using int variables in your sketch. These are 16 bit signed variables and their value can, therefore, be negative

Secondly, in your calculation, do any of the results ever exceed the maximum value that a signed 16 bit integer can hold ?

Since you are using Arduino function, you should use it as intended

try to compile and run this to understand what overflow can do


void setup() {
  Serial.begin(115200); Serial.println();
  int frq = 6248;
  int inp = 19;
  int duty = (frq * 10) / inp;
  Serial.println(frq * 10); // ==> -3056
  Serial.println(duty); // ==> -160
}

void loop() {}

your challenge is what @UKHeliBob mentioned


the issue is not there. the input is really within the 0 - 1023 range and the output range is well within the int representable range. The maths within the function is done with longs, so will handle some level of overflow.

I beg to differ. The fact that long is pushed into int makes the math to be performed as int and not long which leads to overflow

The map function performs its calculation using long, and returns a long, which is then stored in an int. The numbers being used will not cause a problem with that. The error the OP is seeing is caused by int overflow in their equation.

Not sure what you mean, the maths in the map() function are performed as long, and parameters are promoted from int to long which is lossless.

in general yes, assigning a long (the result of the map() call) to an int could be a problem, but not here.

the value submitted into map is the result of an analogRead() so between 0 and 1023 and the map does map that full range map(pot0, 0, 1023, 6249, 118); into something ([118, 6249]) that fits on an int. So the conversion of long to int when you assign the result of the call to map will not create any issue. (same for the other [19, 100] is within range of an int).

the issue is later on, when OP does freq * 10, that's when the overflow happens.

This: UUnnhN - Online C++0x Compiler & Debugging Tool - Ideone.com

yes, In the example you give the long returned value is beyond the bounds of an int. (that's what I meant with "in general yes, assigning a long (the result of the map() call) to an int could be a problem,".

But here you are 100% sure this is not the case as the input to the map function is always within the two provided x_min x_max, you are sure that the output will be within y_min y_max. As those 4 values are compatible with an int, there is no issue assigning the result back to an int.

Bad example and wrong compiler, here sketch.ino - Wokwi ESP32, STM32, Arduino Simulator

Try to add a print, you'll see all is well

  int frq1 = mapp(); //<-------- doesnt match return type
  Serial.println(frq1); // <== OK 6249

the calll to the function is NOT the issue.

later in int duty1 = (frq1*10)/19; the variable frq1 is an int, so the math is conducted int int, and frq1*10 overflows. writing int duty1 = (frq1*10l)/19; would solve it because 10l is a long, so the whole thing would be promoted to long calculation

but when you do int duty2 = (frq2*10)/19; , frq2 is a long, so the maths are conducted in long format and frq2*10 does not overflow.

Your code:

long mapp()
{
	return 6249;
}

void setup() {
  Serial.begin(9600);
  int frq1 = mapp(); //<-------- doesnt match return type
	long frq2 = mapp(); //<-------- matches return type
	int duty1 = (frq1*10)/19;
	int duty2 = (frq2*10)/19;
  Serial.println(duty1);
  Serial.println(duty2);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Still a bad example. If you print frq1 and frq2 you will see the correct number. It is the multiplication by 10 that is overflowing the int, and that will occur regardless of whether you call the map (or in this case the mapp) function.
< edit > If you cast frq1 to long in the equation then the answer will be correct, the problem is directly caused by the compiler defaulting to int math since both frq1 and 10 are int. You can completely eliminate the function call to mapp, and just directly initialize frq1 and frq2 to 6249, and the results will still be incorrect for duty1.

I never said it was. If you do not declare frq as long, which is what map returns, the duty arithmetic is not promoted to long and you get overflow

You are implying that the error is caused by storing the long as int, which implies the call to the map function is part of the problem. That in itself is completely irrelevent to the actual problem.

Again a bit misleading, implying that the map function has something to do with the actual problem. It is fine in this case to store the return value as an int, as long as it is cast to long in the equation (or the 10 is explicitly declared to be long by using 10L).

No... if you don't declare it long, the math frq1*10 is conducted as an int. That's why it overflows. This has nothing to do with map and its returned value;

proof point: you can Keep it as an int, but use frq1*10l and you'll see all is fine... (because the compiler sees 10l as a long, so promotes frq1 to long before doing the math using long arithmetic)

This is what I've been trying to tell, in my own way. If OP matched the return of the map type it would have been long and without knowing everything would have been fine...If only one would follow the return type...

OK, not sure why you brought map() into the mix and in your examples.
it has nothing to do with map as demonstrated by frq1*10l working completely fine, even if frq1 is an int and not a long.

it's all about integer math overflow.

because

OK... that was confusing at least for me as map() is not the issue. it's perfectly OK to store the result of the map() call into an int. The language has precise rules on how this will be dealt with if you overflow (and there was no possible overflow from map in the current code)

In this case yes (well no, as we discussed, but yes), but what if the map range was different? As the rule of thumb matching return type shouldnt land you in trouble, like in this case, even though it is not map related directly