В моем 32-битном встроенном приложении С++ мне нужно выполнить следующее вычисление:
calc(int milliVolts, int ticks) {
return milliVolts * 32767 * 65536 / 1000 / ticks;
}
Теперь, поскольку int на моей платформе имеет 32 бита, а milliVolts имеет диапазон [-1000: 1000], часть milliVolts * 32767 * 65536
может вызвать целочисленное переполнение. Чтобы этого избежать, я разделил коэффициент 65536 на 1024, 32 и переупорядочил следующим образом:
calc(int milliVolts, int ticks) {
return milliVolts*32767*32/1000*1024/ticks*2;
}
Таким образом, пока порядок умножений и делений сохраняется компилятором, функция будет правильно вычисляться.
Состояние Кернингхана и Ричи в разделе 2.12 "Язык программирования C" (у меня нет стандартной версии С++):
C, как и большинство языков, не указывается порядок, в котором оцениваются операнды оператора.
Если я правильно понимаю это, компилятор может изменить свое тщательно выбранное выражение во что-то, что не будет работать должным образом.
Как я могу написать свою функцию таким образом, чтобы она работала?
РЕДАКТИРОВАТЬ: Несколько ответов ниже предлагают использовать вычисления с плавающей запятой, чтобы избежать этой проблемы. Это не вариант, потому что код работает на процессоре, который не имеет операций с плавающей запятой. Кроме того, вычисление выполняется в жесткой части приложения в реальном времени, поэтому ограничение скорости использования эмулируемой плавающей запятой слишком велико.
ЗАКЛЮЧЕНИЕ: С помощью ответа Мердада и комментария Мэтта Макнабба мне удалось найти соответствующий раздел в разделе K & R, раздел A7, где говорится:
Приоритет и ассоциативность операторов полностью определены, но порядок оценки выражений с определенными исключениями undefined, даже если подвыражения связаны с побочными эффектами. То есть, если определение оператора не гарантирует, что его операнды оцениваются в определенном порядке, реализация может свободно оценивать операнды в любом порядке или даже чередовать их оценку. Однако каждый оператор объединяет значения, создаваемые его операндами, таким образом, который совместим с разбором выражений, в которых он появляется. Это правило отменяет предыдущую свободу для переупорядочения выражений с операторами, которые являются математически коммутативными и ассоциативными, но не могут быть вычислительно ассоциативными. Это изменение влияет только на вычисления с плавающей запятой вблизи пределов их точности и ситуаций, когда возможно переполнение.
Итак, Мердад прав: беспокоиться не о чем.