Неявное преобразование в 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 зависит от реализации, поэтому трудно точно сказать, что происходит.