perasperaadastra wrote: ↑23 May 2017 04:57
Подскажите пожалуйста, как компилятор отнесется к следующему куску кода?
Code: Select all
uint8_t varX = 0;
uint16_t varY = 4 * (99225000 + (2*varX-1)*100000) / 32768;
Конкретно, меня интересует то, как компилятор посчитает (2*varX-1).
Переменная varX имеет "маленький" тип uint8_t. Это значит, что еще до начала каких-либо вычислений она будет безусловно подвергнута integral promotions, т.е. ее значение будет неявно приведено к типу int. Языки С и С++ никогда не выполняют арифметические вычисления в "маленьких" типах (за редким исключением), а всегда сначала неявно приводят "маленькие" типы к типу int (или, в экзотических случаях, к unsigned int). А константы 2 и 1 и так имеют тип int. То есть подвыражение (2*varX-1) будет вычисляться в домене
знакового типа int и его результат будет иметь знаковый тип int.
То же относится и ко всему остальному выражению, если предполагать, что константа 99225000 помещается в тип int на данной платформе. (Если нет, то она получит тип long. Сложение 99225000 + выполнится в домене типа long и остаток выражения вычислится в домене long).
Только в самом конце результат вычисления (типа int или long) будет сконвертирован к типу uint16_t. Такая конверсия определена языком и сводится к приведению по модулю (UINT16_MAX + 1).
perasperaadastra wrote: ↑23 May 2017 04:57varX и varY обе беззнаковые переменные. Если не делать кастинг вручную, сочтет ли компилятор, что (2*varX-1) это тоже беззнаковый результат?
Нет. Integral promotions неявно приведут малый беззнаковый uint8_t к
знаковому типу int. Вычисления будут делаться в знаковом типе и результат будет знаковым.
perasperaadastra wrote: ↑23 May 2017 04:57Если я хочу убедиться, что всё будет нормально, правильно ли я понимаю, что нужно сделать так: (2*(int8_t)varX-1)
Зависит от того, что имеется в виду под "нормально". Выбор типа int8_t - странен, ибо его диапазон не покрывает диапазон исходного типа uint8_t. Если хочется знаковых вычислений, то лучше уж взять тип int для явного приведения, особенно если учесть, что константы 1 и 2 имеют тип int. Но необходимости в явном приведении нет.
Если же хочется все вычислять в беззнаковом домене, то надо просто сделать так, чтобы все операнды были беззнаковыми
Code: Select all
uint16_t varY = 4u * (99225000u + (2u*varX-1u)*100000u) / 32768u;