Комментарии 139
Во всех языках есть команда деления с остатком, которая соответствует одной операции, возьмите остаток и сравните с делителем…
Флаг — это ноль или один это и нужно прибавить\вычесть к результату для округления.
ЗЫ: Я не раз встречал алгоритмы выглядящие прекрасно с математической точки зрения, но физическая реализация ужасна — как и тут.
Операция деления не самая убийственная — она по времени занимает столько же, сколько и операция умножения. А операция умножения, столько же, сколько в сумме операция обнуления регистра TEMP[2] и сложения. В случае того оборудования, с которым доводилось иметь дело мне, и которое не поддерживало арифметику с плавающей запятой, — 31мкс.
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/BABBCJII.html
Если так раздражает деление, а операция сдвига не выполняется совсем или выполняется медленно, возможен иной алгоритм: (A+A+B)/(B+B). Этот алгоритм сразу на выходе даст округленный результат.
Следовательно, получается формула ещё проще A/B+Остаток>4
Формула, приведенная Вами, какая-то непонятная…
a/b=x'(огруг.) = x +(d*(x+1)-a<=a-b*x), где x — частное с отсечённым остатком.
Это где-то 6 действий, но тут надо по времени смотреть. Теперь мне кажется, что алгоритм из поста даже быстрее будет.
Конечно. И тот, который расписал в дважды заминусованном посте habr.com/post/412613/#comment_18709951, состязается с ним по времени, потребляя при этом чуть меньше ячеек для промежуточных вычислений. Там одна команда деления, остальные команды — команды сложения
((A+A)/b+1)/2
4 операции, 1 ячейка памяти
С учётом знака ставится аж 5 операций и один переход. Уже можно сравнивать производительность)
((A+A)/b+1 [if(sign1!=sign2) -2])/2
А сравнение знаков в Cи смотрится классно)
Тем не менее, алгоритм я бы несколько модифицировал:
1. Путем сложения удваиваем делимое.
2. Сверяем знаки делимого и делителя: если знаки не совпадают (исключающее или) вычитаем из делимого значение делителя, в противном случае, прибавляем его.
3. Путем сложения удваиваем делитель.
4. Выполняем деление.
Вуаля.
p.s. я вам выше написал свой алгоритм в 4 операции и 1 ячейку памяти.
Не минусовал, но всю жизнь писал, как yizraor, ваш вариант даже в кошмарном сне бы не приснился: вторая операция деления вместо сдвига — это ужасно.
На Си это просто
result = (x + (y>>1)/2) / y
или даже
result = (x + y/2)/y
Операция сдвига практически бесплатна в отличие от деления.
Мне тут другой алгоритм пиарили, так время его выполнения на 20 с лишним процентов выше, чем описанного мной в публикации.
Если программируете ПЛК или микроконтроллеры, то у меня к Вам вопрос: всегда ли анализируете скомпилированный код и оцениваете время выполнения написанной процедуры?
ПЛК и микроконтроллерахКстати, а что Вы понимаете под словом микроконтроллер? Полагаю, что большинство здесь присутствующих под этом термином подразумевают однокристалку, а Вы?
Во-первых, это «железо» Си напрямую не потребляет — то, что написано на Си, компилируется в язык тех команд, которые это железо поддерживает.Никакое железо Си напрямую не «потребляет», так же, как и другие языки программирования, а исполняет программу в машинных кодах. Впрочем, подозреваю, что то «железо», с которым Вы работаете, вовсе не железо, а некая программная система, выполняющая Ваш код, и ее система команд к системе команд того, действительно «железного» процессора, на котором все это крутится, не имеет ни какого отношения. Если я не прав в своих подозрениях, то поправьте меня, но я не представляю себе архитектуру процессора, выполняющего сдвиг дольше умножения и, тем более, деления.
Кстати, а что Вы понимаете под словом микроконтроллер? Полагаю, что большинство здесь присутствующих под этом термином подразумевают однокристалку, а Вы?
И я про них же)
Если я не прав в своих подозрениях, то поправьте меня
Поправлю) Об архитектуре АЛУ процессора ПЛК я говорил в комментариях предыдущей своей заметке: «Анализ бита под «результирующие битовые операции» со словами не подпадает, как и операции модифицирующие значение отдельного бита. Под «результирующими битовыми» мной имелись в виду именно операции побитового сдвига слова, так как операции с отдельно взятыми битами выполняются в битовом аккумуляторе — стеке логических операций, а операции со словами уже в другом аккумуляторе, с пословной организацией.»
Когда-то давно процессор ПЛК имел АЛУ только с битовым аккумулятором, и вот тогда все операции со словами выполнялись очень медленно, команды умножения/деления в особенности.
Если бы была «прослойка», трансформирующая написанную программу в иную систему команд, средство разработки не смогло бы восстановить оригинал.Почему? Вы пишете на некоем «ассемблере» (в Сименсе — это STL, в большинстве других ПЛК — это IL, суть одна и та же), дальше Ваша программа транслируется в исполняемый байт-код, который Вы и загружаете в ПЛК. Но это исполняемый код для исполняющей системы, а не машинный код процессора. Именно из этого бай-кода средство разработки и восстанавливает оригинал. Как некий аналог, можно привести в пример исполняющую систему Java, там Вы тоже можете получить некое подобие ассемблера, описывающего исполняемый байт-код и даже восстановить из него исходный текст, но этот ассемблер не имеет ничего общего с системой команд реальной «железной» машины, на которой выполняется.
PS. Никакое железо Си напрямую не потребляет, потребляется машинный код, а уж во что Си[++] будет транслирован — зависит от компилятора и его флагов. Иногда они «умные», иногда не очень — когда нужна скорость приходится смотреть на результат и пробовать разные варианты.
Вот я и не пользуюсь Си, чтоб результат не зависел от таланта или его отсутствия у разработчика компилятора, а пишу как можно ближе к железу, дабы достичь минимального времени выполнения программных процедур.
Но речь в публикации, собственно, не об этом, т.е. не о времени и не о конкретном способе округления, а о том, что, то об округлении позабудут, то об ограничениях, накладываемых размерностью чисел. А куском, акцент действительно получился на втором.
"О микроконтроллерах" — операция сдвига примерно всегда есть:
- AVR: LSR для 8-битных беззнаковых, ASR для знаковых, для чисел 16+ бит — ROR для всех байтов кроме старшего, итого 1 однотактовая операция на байт
- ARM — то же самое для 32-битных
- 8051 — RRC; PIC — LSRF/ASRF/RRF
- MSP430 — RRA/RRC
Деление может вообще отсутствовать (AVR) или требовать больше тактов (Cortex M3 — от 2 до 12, и это ещё дёшево!)
Деление — сложная и дорогая операция, его все стараются заменить умножением или вообще сдвигами. Поэтому за лишнее деление вас заминусуют на автомате, и вам надо очень чётко объяснить, почему именно для вашего случая деление выгоднее.
Что касается ПЛК — с ними не работал. Вероятно, там есть свои особенности (как я понимаю, прямого доступа к МК нет, всё довольно медленно и на фоне этого теряется разница между делением и сдвигом, если он есть — так?). Вот если бы вы акцентировались на специфике конкретного ПЛК (ну там, разработчики ПЛК сдвиг не дали) — другое дело. Но даже если нет операции сдвига — не вижу проблемы использовать стандартный алгоритм (прибавить половину делителя перед операцией), будет два деления, как и у вас. Разве что операций перемещения может оказаться больше, но это опять же случай, когда надо разобрать и показать, почему надо использовать ваш алгоритм вместо стандартного.
Да, это, безусловно, так.
А я в своём первом комменте пытался объяснить, за что могут рефлекторно минусовать. Сам был шокирован, но не настолько, чтобы минусовать (даже если бы была возможность) — скорее объяснить, как обычно делают и почему.
Да, возвращаясь к вопросу "всегда ли анализируете скомпилированный код и оцениваете время выполнения написанной процедуры" — только "узкие места". Нет смысла оптимизировать всё.
А делений избегаю рефлекторно. Привычка ещё с 8080.
По поводу минусования… — как думаете, за что вот этот мой комментарий ажно дважды отминусовали habr.com/post/412613/#comment_18709951?
Судя по приводимым таблицам, где умножение и деление имеют равные времена по 450 нс — деление там выполняется каким-то хитрым встроенным блоком, который работает отдельно от основной логики и по характеристикам сходен с высокоуровневым процессором.
Иначе я этот феномен объяснить не могу — потому что для современного компьютерного дела открытие однотактного деления 32/32 бита это что-то на уровне доказательства Большой теоремы Ферма.
Но тогда непонятно, накойхер там вообще логика этого ПЛК, если есть хороший процессор…
Интерпретатор же. ПЛК нужно не быстродействие, а простота и надёжность.
Почему процессор так быстро считает, предположить можно — он, скорее-всего, на ПЛМ реализован, т.е. операции поразрядного сложения и вычитания со сдвигом заведомо аппаратно реализованы на вентилях. Остается выдернуть операнды из памяти в процессор, активировать соответствующую цепочку логических элементов, и результат записать обратно в память. То есть основное время занимает загрузка-выгрузка из памяти. Это хорошо объясняет то обстоятельство, что время выполнения команд с 32-битными операндами мало отличается от 16-битных.
Ну и что? Если напрямую звать его команды нельзя?
В калькуляторе тоже внутри есть процессор, но команды напрямую не позвать. О кофеварке или автомобиле я вообще молчу (хм… а сколько процессоров в автомобиле?)
То, что это уже никак не «простота и надёжность», ради которой отказываются от процессора.
Даже если он всего лишь выполняет умножение и деление, это серьёзный модуль внутри.
И тогда возникает вопрос, а зачем вокруг делать обвязку, которая убивает 99% возможностей процессора.
Представьте себе обычный микроконтроллер (процессор с встроенной памятью, удобными GPIO ногами и небольшой кучкой периферии). Можно дать юзеру возможность напрямую управлять им через ассемблерный код — но, к примеру, это даст ему возможность подать +5V на ногу, которая в данный момент подключена к земле. Или написать бесконечный цикл и подвесить проц (не факт, что юзер сделает правильную настройку watchdog).
А можно — ограничить его, дав лишь очень урезанный набор команд, после каждой команды отслеживать состояние системы и т.п. — не давая ему написать и выполнить опасный код, и упрощая написание (за счёт урезания возможностей).
Как думаете, что безопаснее?
Простота и надёжность — не простота процессорного модуля, а простота написания кода (и вытекающая из этого бОльная надёжность кода). И во многих задачах "убить 99% возможностей" — окупается.
У них просто своя специфика: с одной стороны, программы проще, с другой — за баги больнее наказывают (повисший Ворд просто перезапускается; повисшая котельная зимой даст замёрзнуть трубам)
за баги больнее наказываютОтличный повод использовать платформы, под которые можно писать только на Ассемблере.
-
Автор, Вы пишете: "… округление результата до ближайшего целого организовать элементарно. Для этого достаточно удвоить остаток деления, просуммировав его сам с собою, а затем вновь поделить его на то же число..."
Прошу простить, но считаю Ваш вариант неэффективным способом.
Я в своё время придумал другой: прибавить к числу половину делителя, затем выполнить само целочисленное деление.
Если делим в цикле на одно и то же число, то половину делителя можно вычислить один раз. В противном же случае: целочисленное деление пополам — это битовый сдвиг на 1 позицию вправо, что тоже очень быстрая операция :)
Под работу с отрицательными числами данный способ расширяется без особого труда: добавится условный переход, но это лучше чем второе деление.
Пример для дроби 64/13:
(64 + (13 / 2)) / 13 = (64 + (13 >> 1)) / 13 = (64 + 6) / 13 = 70 / 13 = 5
Код кажется длиннее лишь потому. что реализован в формате подпрограммы, которую можно вызвать сколь угодно раз.
Отсутствие поддержки операций сдвига не самое ужасное. Как-то пришлось решать задачу с вычислениями на контроллере, который умножать два 16-битных числа умел, а поделить результат умножения — нет, и его пришлось такой арифметике программным способом обучать.
Так и пишите, что так мол и так — столкнулся с экзотикой, на которой сдвиг дороже деления 8-O (на этом месте вы уже привлекли внимание аудитории), поэтому вместо общепринятой реализации округления сделаем такой костыль.
Что касается экзотики, то никакой экзотики нет. Это норма для такого железа, когда время выполнения операции сдвига вдвое превышает время операции деления сдвоенных целых чисел.
К слову, даже алгоритм, о котором говорит yizraor, с меньшим числом операций деления, отнимает по времени на 20 с лишним процентов больше, чем тот, который озвучил в публикации.
полуготовые игрушки типа Ардуин и Рапсберри в промышленности не годятсяСогласен, но как на счет МК типа STM, например? Они вроде тоже в промышленности применяются.
они как модульный конструкторА зачем совмещено конструирование и программирование? Если идет ориентация на программирование — можно ведь запросто реализовать в железе все элементарные операции, это доказано на практике. Если на конструирование — берите да собирайте схемы из захардкоженных логических элементов, зачем в них еще что-то дописывать? Я что-то не так понимаю?
Ни о каком конструировании при сборке ПЛК речь не идет — просто его конфигурация «нанизывается» из модулей — базового, с процессором внутри, и модулей расширения. Есть и другой конструктив — на шасси. Гибкость, универсальность и быстрый ремонт модульной заменой, вот что ставится во главу угла.
Те кто переходит от разработки электронных схем, печатных плат и Embedded Soft к микроконтроллерам, очень быстро ощущают разницу в скорости разработки, в суммарных затратах на разработку решения и эту самую гибкость в пользу ПЛК, после чего обычно напрочь отказываются от разработок и создания электроники под конкретную задачу.
То, что «многим непонятно», уже отметил для себя.
Для ПЛК тоже можно писать программы на Си-подобном языке, только код будет неоптимален и совсем не похож на код исходника.ключевое слово — на Си-подобном.
Скорость разработки программы зависит больше не от того, на чем написано, а от того, кто пишет.Естественно, если человек не умеет программировать, то никакой язык высокого уровня ему не поможет. По поводу того, что уровень языка имеет мало значения — Ваш тезис опровергается всей историей развития программирования. Кстати, тот же C, ранее считавшийся языком высокого уровня, с некоторых пор перестал считаться таковым. Но в области микроконтроллеров для C пока нет полноценной замены (да, про Rust знаю, но он еще слишком молод для повсеместного использования).
Но вот в части разработки программ для ПЛК с начала 2000-х Си фактически укокошил профессию программиста систем автоматики и продолжает добивать средства разработки программ для ПЛК, из которых стараниями нерадивых и необразованных менеджеров, которые слыхом не слыхивали о сквозной программной совместимости. стали изымать прочие языки, входящие в стандарт IEC. Написанные прежде программы обновленными версиями софта больше не открываются, а годами наработанные библиотеки готовых элементов и процедур вмиг превратились в труху. Просмотр целиком скомпилированного программного кода, написанного на языках, отличных от Си, больше невозможен — только мелкими кусочками и т.д., и т.п.
Когда в начале 2000-х с Запада надуло поветрием брать на работу Си-программистов взамен программистов ПЛК, а вместо самих ПЛК ставить ПК с силиконовым диском, вторая категория работников вынуждена была оставить свой привычный труд… Чуть позже у сишников, трудящихся в иных сферах, выросли зарплаты и новоявленные программисты систем автоматики хлынули туда, где больше платят, ПК с силиконовыми дисками, не прослужив и двух лет стали резко сыпаться, решили вернуться к старым, добрым ПЛК, а программистов-то, настоящих, знающих линейку с которой нужно работать, уже ищи свищи. Тогда и пришлось приделывать примочку к пакетам разработки в виде компилятора с языка Си. Теперь программистов на Си пруд-пруди, а заработки в профессии сильно упали.
Теперь программистов на Си пруд-прудиЗнание языка C для программиста сродни знанию латыни для врача. Но от этого не стоит считать любого программиста, знакомого с этим языком, а их действительно — большинство, именно C программистами, коих сейчас не так и много на фоне всех других языков.
код будет неоптималенЭто говорит о том, что в 2018 году для обсуждаемой архитектуры еще нет нормального C-компилятора — еще одна причина оставить эту архитектуру в прошлом.
и совсем не похож на код исходникаПро это не очень понял. Он ведь и не должен быть похожим, то С, а то Ассемблер.
Скорость разработки программы зависит больше не от того, на чем написано, а от того, кто пишет.Если Вы способны писать качественный код на Ассемблере с той же скоростью, что и на С, то мне остается только Вас поздравить. Но мне все же кажется, что большинство людей в мире не такие.
гибкость в пользу ПЛКОтсутствие операции побитового сдвига — это гибкость?
Если у меня листинг программы самописный, составленный непосредственно из инструкций ПЛК, то в этом случае вообще никаких багов. Баги появляются тогда, когда программа составляется в IEC IL, или графических Ladder Logic/FBD, вот тогда компиляция уже зависит от фантазий разработчика пакета программирования. Если же она написана на языке высокого уровня (ST), то тут вообще широкое поле для фантазии — коду после компиляции можно удивляться бесконечно.
то в этом случае вообще никаких баговНу это да, если, конечно, не считать тех, что вы сами создали.
коду после компиляции можно удивляться бесконечноЕсли так, почему никто не взял да не написал нормальный пакет программирования?
Ну это да, если, конечно, не считать тех, что вы сами создали.
Сам я сразу пишу работающий код.
Если так, почему никто не взял да не написал нормальный пакет программирования?
А кто это должен делать? Сами программисты ПЛК?
Лет 7-8 назад был пакет программирования с малым количеством изъянов. От него сперва отказались, заменив другим, намного более убогим, выход которого анонсировали в течение лет эдак 10-ти (анонсировали, конечно, не убогий, а передовой). А через некоторое время из этого убогого ещё и изъяли все текстовые языки, кроме ST. И новые контроллеры программируются только новым софтом. Все персональные, годами наработанные библиотеки и старые программы, написанные текстом, в нем больше не открываются. Так что имеем то, что имеем.
Сам я сразу пишу работающий код.ОК))
А кто это должен делать?Ну, наверное Вы, раз умеете сразу работающий код писать)
От него сперва отказались, заменив другим, намного более убогимЗачем?
А через некоторое время из этого убогого ещё и изъяли все текстовые языки, кроме ST.Ну так не обновляйте ПО.
Все персональные, годами наработанные библиотеки и старые программы, написанные текстом, в нем больше не открываются.Если это текст, то его открыть можно любым блокнотом. Или компилировать нечем?
А кто это должен делать?
Ну, наверное Вы, раз умеете сразу работающий код писать)
) Так в настоящее время я ж прикладной программист, программист ПЛК, а не разработчик IDE на языках верхнего уровня
От него сперва отказались, заменив другим, намного более убогим
Зачем?
Насколько знаю, тупоголовыми менеджерами двигали две вещи: 1) мечтали объединить два софта (один предпочитали и использовали в Азии, другой в Европе) в единый пакет, в итоге же, получили не единый пакет, а три раздельных; 2) старый пакет разрабатывался партнером, с которым, как и с другими многолетними партнерами, новый менеджмент корпорации сумел рассориться…
А через некоторое время из этого убогого ещё и изъяли все текстовые языки, кроме ST.
Ну так не обновляйте ПО.
Увы и ах, новое оборудование поддерживается только обновленными версиями.
Все персональные, годами наработанные библиотеки и старые программы, написанные текстом, в нем больше не открываются.
Если это текст, то его открыть можно любым блокнотом. Или компилировать нечем?
В новом компилировать нечем. Ни программные куски, ни библиотеки, написанные текстом, в программу, созданную в этом пакете даже не добавить.
Да, во всем виноват компилятор)А под средствами разработки, помимо собственно компилятора, я подразумеваю и сами языки программирования, и все прочее, что объединяет IDE.
Понял Вас.
Прошу простить за скептическое отношение.
Сам микроконтроллеры не программировал, и тонкостей не знал (про отсутствие сдвига на некотором железе), поэтому судил со своей x86-й колокольни :)
Целочисленная арифметика. Делим с округлением результата. Часть 1