Неявное преобразование в float с использованием avr-gcc: uint8_t vs. uint16_t

У меня есть вопрос относительно неявного преобразования uint8_t а также uint16_t используя Arduino IDE 1.8.2 (gcc 4.9.2.). Аппаратное обеспечение стандартное Arduino (ATMega328p).

Я написал кусок кода, используя uint8_t и решил потом переключиться на uint16_t, (Должен был увидеть, что грядет...)

Тем не менее, они, кажется, ведут себя немного иначе для неявных преобразований, что вызвало ошибки в программе.

Минимальный рабочий пример:

void setup()
{
  uint8_t x = 15;
  uint8_t y = 5;
  float myF = y-x;

  Serial.begin(74880);
  Serial.println(myF);
}

Это напечатает -10.00 на моей последовательной консоли.
Это хорошо и то, что я ожидал.

Однако, если я изменю x (или же x а также y) чтобы uint16_t результат будет 65526,00! Если я изменю myF от float до int я снова получаю -10. (Я никогда не меняю ни одно из значений)

Поскольку я сохраняю результат в подписанном типе данных, я предполагаю, что компилятор понимает возможность отрицательных значений и "обрабатывает ситуацию правильно" (сохраняет знак, как в случае int) или выводит предупреждение в случае, если это не так. t счастлив о несовпадении типов данных. Однако, даже если уровень предупреждения установлен на "все", предупреждение никогда не показывалось. Поэтому я предполагаю, что компилятор знает, как справиться с ситуацией без потери знака / данных.

Кроме того, поскольку он работает с целевым типом данных int, меня удивляет, что он не работает с большим числом с плавающей точкой.

Я проверил ситуацию на моей системе x86 - gcc 4.7.3 сохраняет знак. Однако в мире 8-битных микроконтроллеров AVR могут применяться другие правила / условия. (?)

Так что там происходит? Может быть, кто-то с большим знанием компилятора может помочь здесь..
(Я знаю, что мог бы избежать этой ситуации путем явного приведения, но поэтому я должен был бы знать об этой ловушке.
Поэтому я хотел бы знать, чем именно это вызвано, так как это действительно было неожиданностью при переходе от uint8_t в uint16_t..)

Я читал, что согласно правилам преобразования целых чисел "целочисленные типы, меньшие, чем int, повышаются до int, когда над ними выполняется операция". Я предполагаю, что avr-gcc следует целочисленным акциям. (?) Итак, я понимаю, что фактические вычисления обычно выполняются в int в любом случае, а затем преобразуются в целевой тип данных (в данном случае с плавающей запятой). Проблема в том, что uint16_t имеет равный, но не меньший размер, чем 16-битное int AVR, и поэтому uint16_t не может быть повышен? Если так, то почему он работает с целевым типом int?

И почему он работает с int в качестве целевой переменной, а не с 4-байтовым плавающим числом? И почему это не предупреждает?

2 ответа

Решение

Так что там происходит?

Целочисленные акции

Если int может представлять все значения исходного типа..., значение преобразуется в int; в противном случае он преобразуется в unsigned int, Они называются целочисленными акциями.
C11 §6.3.1.1 2

С ниже, y-x продвигает каждый x,y в int и вычисляет 5-15, который int -10 и присваивает эти значения mF,

  uint8_t x = 15;
  uint8_t y = 5;
  float myF = y-x;

С ниже, y-x продвигает каждый x,y в unsigned и вычисляет 5u-15u, который unsigned 65526u и присваивает эти значения mF,

  uint16_t x = 15;
  uint16_t y = 5;
  float myF = y-x;

Зачем unsigned и не int? uint16_t не соответствует "если int может представлять все значения исходного типа "условие на 16-битном int платформа при продвижении uint16_t,

Никаких загадок, только целочисленные акции на 16-битной платформе int/unsigned

Вы используя целые числа без знака, чтобы пойти отрицательным приведение этих неподписанных типов к float, который не определено поведение, определяемое реализацией. Используйте int8_t или int16_t для правильной обработки отрицательных значений. Различие в поведении, где приведение к плавающей точке от uint16_t по сравнению с приведением от uint8_t зависит от реализации, поэтому трудно точно сказать, что происходит.

Другие вопросы по тегам