Я пытаюсь запрограммировать следующее на C, используя MPLABX IDE и компилятор XC8, для микроконтроллера PIC12. Я раньше не программировал ни микроконтроллеры, ни C, поэтому есть некоторые новые вещи, которые мне нужно учитывать. Одним из них является то, что вычисления с использованием типов с плавающей запятой неэффективны на этих микроконтроллерах, и поэтому их следует избегать. Пытаясь следовать этой философии, я хотел бы узнать, как лучше всего выполнять такие вычисления:
a = b / c * d
Где:
0 =< a =< d,
0 =< b =< c,
0 =< c,
0 =< d
a, b и c — 16-битные целые числа без знака. d может быть выбран свободно.
Я ищу хороший компромисс между хорошим разборчивым исходным кодом и эффективным скомпилированным кодом.
Я думал о реструктуризации уравнения, но избегание одной проблемы приводит к другой:
a = b / c * d
—> b/c всегда будет 0. Остаток потерян.
a = b * d / c
—> b*d может привести к переполнению.
Выполните умножение перед делением, используя более крупный шрифт, если будет переполнение. a = (int)((long long)b * d / c);
Если вы хотите округлить результат, вы можете добавить (или вычесть, если произведение отрицательное) половину делителя c >> 1
перед делением. — person K0ICHI schedule 07.10.2019
Не могли бы вы указать, какой результат вы хотите. Если a
является целым числом, вполне возможно, что оно не может точно хранить результат деления. Вы хотите обрезать? круглый? Вам нужен остаток или можно отказаться от него? — person K0ICHI schedule 07.10.2019
@Флюгер. Большое спасибо, это решение, на которое я надеялся, даже в одной строке кода! Почему бы не сделать это ответом? Я приму. Не могли бы вы также сказать мне, могу ли я считать само собой разумеющимся, что компилятор будет выполнять математику в том же порядке, в котором я его ввел? — person K0ICHI schedule 07.10.2019
Re может считать само собой разумеющимся, что компилятор будет выполнять математические операции в том же порядке, в каком я его ввел. В общем, определенно нет. Но ассоциативность операторов требует, чтобы умножение происходило перед делением, потому что оно слева. — person K0ICHI schedule 07.10.2019
Вы должны сделать умножение перед делением, используя более крупный шрифт, если будет переполнение.
Если вы сомневаетесь в последовательности операций, вы можете заключить ее в скобки.
хотя в этом нет необходимости, потому что хотя
*
и/
имеют одинаковый приоритет, они анализируются слева направо.Если вы хотите округлить результат, вы можете добавить (или вычесть, если произведение отрицательное) половину делителя перед делением.
С вашим
0 =< c
следите за делением на0
. — person K0ICHI; 07.10.2019Минор: если цель равна половине делителя, то почему
(c >> 1)
вместоc/2
? Кстати, идея сложения/вычитания зависит не только от знака продукта, но и от знака продукта, и от знакаc
. — person K0ICHI; 07.10.2019@chux согласился, что это лучше оставить компилятору, но мой (не недавний) опыт работы с компиляторами PIC невелик. Кроме того, все значения в вопросе неотрицательны. — person K0ICHI; 07.10.2019