Главная |
Компьютерные тренажеры
|
SCADA |
Другие проекты
|
Публикации
|
Об авторе |
Донской А.Н.
10 августа 2007
ТРИЗ В ПРОГРАММИРОВАНИИ
Вот по случаю вспомнил про ТРИЗ (Теорию Решения Изобретательских Задач) и решил написать эти короткие заметки.
Возникла тут одна программистская задача, вкратце выглядит так. Есть 16-разрядное микропроцессорное устройство без плавающей арифметики. Есть необходимость масштабирования сигналов. То есть измеренное (в дискретах) 16-разрядное целое надо умножить на масштабный коэффициент (конечно, вещественный в общем случае), чтобы получить значение в нужном диапазоне и в нужных величинах (скажем, в амперах).
Старшее поколение инженеров, начинавшее с аналоговых вычислительных машин (АВМ), хорошо знает задачу масштабирования (приведения в нужный диапазон) и все ее подводные камни. Для тех, кто с этим не сталкивался, поясню, что вся задача как таковая возникает исключительно из-за ограниченности диапазона представления величин. При использовании вещественной (плавающей) арифметики проблема снимается (ну или отодвигается в достаточно необозримые дали, поскольку допустимый диапазон составляет несколько десятков порядков, например, от 1e-36 до 1e36). Однако в 16 двоичных разрядах имеем диапазон всего-то от 0 до 65535, что маловато. Причем, если в АВМ переполнение приводит только к ограничению сигнала, то 16-разрядная арифметика будет давать совершенно непредсказуемые (на человеческий взгляд) результаты. Псувдослучайные, можно сказать. Сложно отлаживать такие ситуации, если заранее не учесть все возможные диапазоны исходных сигналов, калибровочных параметров и промежуточных вычислений.
Подходим к решению задачи грамотно и решаем типовым образом - вещественный по сути масштабный коэффициент раскладывается на пару 16-разрядных целых - множитель m и делитель d. Тогда операция масштабирования в целочисленной арифметике выглядит как умножение 16-разрядных сигнала и множителя m с получением 32-разрядного результата и последующим его делением на 16-разрядный делитель d. Результат опять 16-разрядный.
Как рассчитать коэффициенты m и d (ведь k=1.55555 можно грубо представить как m=16 и d=10, а можно и как m=15556 и d=10000, или еще точнее как m=31111 и d=20000)? Очевидно, что при наибольших возможных значениях m и d обеспечивается наибольшая точность.
В том же случае, когда искомый масштабный коэффициент представляет собой функцию от ряда параметров настройки, вычисление итоговых пар m и d становится особенно веселым и совсем не тривиальным. Например, если m=m1*m2, то при m1=1000 и m2=10000 мы не можем вычислить (и сохранить в 16 разрядах) итоговый множитель m из-за переполнения. Тогда приходится "сокращать нули" путем деления числителя и знаменателя на определенное число (например, m=m1*m2/1000; d=d/1000). При этом может произойти очередная потеря точности (например, d=1234 превратится в d=1).
Наконец, следует упомянуть о дополнительных условиях, которые, хотя и относятся непосредственно к теме (явно выражаются в формулах), но постоянно забываются программистом, перегруженным текучкой (в нашем случае коэффициент k следовало еще всегда умножать на 1000, поскольку выходной отмасштабированный результат должен отображаться на дисплее устройства в формате с фиксированной точкой - 3 разряда после точки).
Зачем я так подробно описываю задачу и проблемы? Во-первых, чтобы показать тем, кому будет интересно, как решаются типовые задачи масштабирования в целочисленной арифметике. А во-вторых, и это главное, показать, как за объемистым нагромождением условий и формул сама задача перестает быть прозрачной; а обыкновенное стремление ленивого программиста, каковыми является большинство из нас, решить ее наскоком, держа все условия в голове, заканчивается позорным блужданием в трех соснах в течение длительного непродуктивно потраченного времени.
Итак, задача давно и успешно решена, для каждой группы сигналов рассчитываются пары коэффициентов, все довольны и все забыли процесс решения задачи давно и прочно.
И в один прекрасный момент, вдруг, возникает проблема. Требуется обслужить еще одну группу сигналов. Пожав плечами, вспоминаем решение задачи и в лоб пишем еще одну формулу для соответствующих коэффициентов m и d, нарываясь с ходу на переполнение в числителе. Еще раз пожав плечами и лукаво не мудрствуя, делим m и одновременно d на 1000 и отдаем на испытания.
Результат обескураживает - на дисплее отображается полный бред. Проверяем, откуда руки у испытателей растут и как они задали исходные параметры. Убеждаемся, что задали неправильно. Задаем правильно и снова получаем бред. Чешем репу в районе затылка и находим ошибку в формуле вычисления m. Теперь все правильно, но отображается тот же бред. При отладке обнаруживается, что делитель d был равен 500, но после того, как его вместе с множителем m поделили на 1000, чтобы избежать переполнения, вместо одной проблемы имеем другую - в знаменателе получился ноль!
Ладно, долго возимся, но путем дополнительных сокращений обеспечиваем что-то путевое и в нужном диапазоне. Отдаем на испытания. Испытатели радуются, но потом обиженно отмечают, что отображаемое значение в 1000 раз меньше, чем должно быть. Головы у всех чугунные, но соображаем достаточно быстро, что забыли m на 1000 умножить (см.выше про отображение с фиксированной точкой). Но позвольте, однако ж, у нас уже в предельном случае m=60000, в то время как d=1! Множитель m на 1000 умножать некуда - переполнение, а делитель на 1000 делить тоже некуда!
Вытираем пот со лба, собираем маленькое производственное совещание (прибор должен быть отгружен на прошлой неделе!), обсуждаем еще раз всю проблему со всех сторон и приходим к выводу, что задача невыполнима. Предлагается ограничить диапазон исходных данных для вычисления коэффициентов, но это нельзя. Останавливаемся на том, что вообще не будем показывать эти сигналы в абсолютных величинах, а только в относительных - все равно у конкурентов только относительные используются.
Тут один товарищ (назовем его "представителем заказчика", хотя это не то, что подразумевалось в советское время; просто подчеркнем, что к процессу программирования устройства в этой его части он имеет весьма косвенное отношение) говорит - а у нас там что? Амперы? И в 1000 раз меньше показывает? Ну так давайте напишем, что это КИЛОамперы, и все будет как надо!
Я обрадовался, он обрадовался, а начальник почесал репу и сказал, что не верит в такие простые решения... Не бывает так, чтобы приходили с глобально фатальной проблемой, а уходили с тем, что и делать ничего не надо вовсе! Тут я сразу про ТРИЗ и вспомнил. ИКР это называется, то бишь Идеальный Конечный Результат. Совершенно классический пример - и делать ничего не надо, и проблема решена!
Проблема: показывает в 1000 раз меньше, чем надо.
Противоречие: можно изменять масштабный коэффициент, но нельзя его изменить из-за переполнения.
ИКР: масштабный коэффициент не меняется, но показания становятся правильными.
Решение: меняем масштаб отображаемых величин за счет перехода к другим единицам измерения.
Спрашивается, сколько бы мы все вместе времени сэкономили, если бы оторвались от текучки и не зациклились, спотыкаясь на хорошо проторенной дорожке, а сразу мыслили по-тризовски от технического противоречия к ИКР?
ССЫЛКИ ПО ТЕМЕ:
www.altshuller.ru - Официальный сайт Фонда Г.С.Альтшуллера (автора ТРИЗ-РТВ-ТРТЛ)Home Page http://simulators.narod.ru |
Post: 428000, г.Чебоксары, а/я 121, Донской Алексей Николаевич |