Умножение, деление. Что не так?

const unsigned long C1 = 30 * 1000;
const unsigned long C2 = (300 * 1000)/C1; // должно быть = 10

void setup() {
  Serial.begin(57600);
  Serial.println("\n-------");
  Serial.print("C1 = "); Serial.println(C1);
  Serial.print("C2 = "); Serial.println(C2);

  unsigned long V1 = (300 * 1000)/C1;   // должно быть = 10
  Serial.print("V1 = "); Serial.println(V1);

  long V2 = (300 * 1000)/30000; // должно быть = 10
  Serial.print("V2 = "); Serial.println(V2);

  int V3 = (300 * 1000)/30000; // должно быть = 10
  Serial.print("V3 = "); Serial.println(V3);
}

void loop() {
}

Arduino UNO напечатано в консоли монитора:

C1 = 30000   (ok)
C2 = 143164  (must be = 10)
V1 = 143164  (must be = 10)
V2 = 0       (must be = 10)
V3 = 0       (must be = 10)

Что не так?

, 👍0

Обсуждение

Взломайте long V2 = (300 * 1000UL)/30000; напечатайте 10, @tim4dev


4 ответа


Лучший ответ:

5
300

и

1000

являются целыми числами.

Что

long V2 = (300 * 1000)/30000;

действительно делает, берет целые числа 300 и 1000, умножает их. Это вычисление переполняется и равно -27680. Разделив это на 30000, получаем 0. То, что вы добавили в своем комментарии, не является хаком, это правильное решение. Включая суффикс L или UL, вы сообщаете компилятору, что вам нужны длинные числа, а не целые.

,

2

Что не так?

Вы предполагаете, что выражение имеет тип long int, но на самом деле это int. Литералы имеют тип int. Вам нужно убедить компилятор, что выражение имеет тип long int. Это можно сделать с помощью приведения:

((long) 30000)

или синтаксис длинного целого числа:

30000L

Компилятор AVR GCC использует int, определенный как 16-битный. Выражение и литералы подразумевают 16-битные.

Дополнительную информацию о представлении типов данных C/C++, выражений и соглашений о вызовах можно найти здесь.

Ура!

,

-2

Это мой код для вашей проблемы:

unsigned long mul(long num1, long num2)
{
  unsigned long res, add = 0;
  while (num1 > 65000)
  {
    num1 -= 65000;
    add++;
  }
  res = unsigned(num1 * num2);
  while(add)
  {
    res +=65000;
    add--;
  }
  return (res + add);
}
,

Это не решает никаких проблем, ни для чего не полезно и потенциально может потребовать огромного количества процессорного времени., @Edgar Bonet


1

Хорошая практика с литеральными числами, которые вы хотите разделить и умножить, — всегда определять их как double и преобразовывать их в целые числа при их использовании. Это часто имеет место с константами частоты/периода. Это предотвращает большинство катастрофических ошибок переполнения/округления, которые могут возникнуть в целочисленной математике, и в то же время позволяет использовать согласованные единицы измерения в ваших определениях.

 #define F_CPU 16000000.0   // в Гц
 #define F_TICK (1.0/F_CPU) // в секундах
 #define DELAY 0.001        // в секундах
 #define COUNT ((uint16)(DELAY/F_TICK))
 Serial.println(COUNT);

Конечно, вы можете вычислить количество тактов процессора в миллисекунду, используя математические вычисления с фиксированной запятой, например, определив частоту в МГц и время такта в наносекундах, но вам придется чесать голову каждый раз, когда вам это понадобится. для преобразования периода в частоту и наоборот, и вам придется менять единицы измерения в тот момент, когда ваша частота не будет кратна МГц.

,