Как я могу вычислить это, используя только целочисленные типы данных?

Я пытаюсь запрограммировать следующее на 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

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 1
  1. K0ICHI

    Вы должны сделать умножение перед делением, используя более крупный шрифт, если будет переполнение.

    a = (int)((long long)b * d / c);
    

    Если вы сомневаетесь в последовательности операций, вы можете заключить ее в скобки.

    a = (int)(((long long)b * d) / c);
    

    хотя в этом нет необходимости, потому что хотя * и / имеют одинаковый приоритет, они анализируются слева направо.

    Если вы хотите округлить результат, вы можете добавить (или вычесть, если произведение отрицательное) половину делителя перед делением.

    a = (int)(((long long)b * d + (c >> 1)) / c);
    

    С вашим 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

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: