Недавно на работе столкнулись с интересной ситуацией, о которой захотелось написать тут, потому что случай довольно интересный, хотя как и оказалось простой. На одном из агрегатов, управляемым контроллером от Allen Bradley Compact Logix L33ER, в контроллере постоянно сыпались предупреждения, а точнее даже минорные ошибки (Minor Faults) - которые на функциональность никак не влияют, но раздражают своим присутствием. В секунду по нескольку десятков таких ошибок без перерыва: Type 04 Program fault (Code 04) Arithmetic overflow. Result of an arithmetic instruction out of range, что переводится примерно как "Арифметическое переполнение. Результат арифметической инструкции вышел за предел."
Проблема возникает в одной подпрограмме, в двух разных местах (Rung 0 и 37), где вызывается один и тот же блок, то есть проблема именно в этом блоке Penetration Control, потому как исключив из программы вызов блока, ошибки прекратились совсем
Внутри этого блока скрывается формула:
Bend_Radius := ((2.0*Penetration*Material_Gage)-(Penetration **2.0)-(Material_Gage **2.0)-(Roll_Spacing **2.0/4.0))/(4.0*(Penetration-Material_Gage));
Yield_Percent :=1.0-((Bend_Radius*2.0*Material_Yield)/(Modulus_Of_Elasticity*Material_Gage));
IFBend_Radius > 0.0
THEN
Bending_Loss :=(3.0*Material_Width*Material_Yield*Material_Gage**2.0)/(2.0*Bend_Radius)*(1.0+(1.0/(86.3-(88.0*Yield_Percent))));
ELSE
Bending_Loss := 0.0;
END_IF;
Первоначальное предположение было, что слишком много знаков после запятой у всех этих Material_Gage или Material_Width, а после возведения в квадрат так и вообще количество знаков становится еще больше. Конечно же REAL тип данных позволяет хранить очень много знаков, и более того, как я понимаю ему все равно сколько там будет этих знаков, он сохранит число, просто с потерей точности, но вызывать ошибки это не должно было бы. Но так как проверить эту гипотезу проще всего, то попробовали немного уменьшить точность вычислений, округлив числа до 2-3 знаков после запятой. Кстати такие странные числа, что на скрине, получаются по причине того, что миллиметры переводятся в дюймы, а другие метрические единицы переводятся в неметрические. В итоге фокус с уменьшением точности ни к чему не привел, ошибки так и продолжали сыпаться.
Тут просто еще одна безуспешная попытка
Немного пробовали повозиться с типами данных. DoubleInt не подошел - может диапазон и больше (8 байт вместо 4, поидее), но сохраняет он только целые числа. Но зато проверили что контроллеру по барабану, что в целочисленный тип хотят сохранить число с плавающей точкой, он просто тупо его округляет (видимо автоматом преобразуется)
А вот идея с выяснением на каком этапе вычислений происходит переполнение, дала свои плоды. Выяснилось что проблема в самой первой формуле, где как раз много возведений во вторую степень. После замены всех Х **2.0 на Х * Х, в первой формуле - ошибки перестали сыпаться. Дальше было уже интересно а какой именно параметр дает такой сбой. И в итоге получилось что Penetration ** 2.0 вызывает переполнение, а Penetration * Penetration нет. Отличие Penetration от других переменных в том, что это число отрицательное. Как только меняем его на положительное число, то ошибок нет, отрицательное - сразу валятся.
Вот такой интересный кейс получился. Я подозреваю что это переполнение связано с ошибкой в прошивке контроллера: возможно неправильно парсится выражение, или еще что. Может быть используются разные инструкции в процессоре, для разного вида записей. Потому я и написал об этом тут, может кто подскажет что это может быть, мне просто интересно. А возможно кто-то столкнется с чем-то подобным и через гугл найдет эту запись, и сможет сразу у себя в проекте пофиксить такую ошибку переполнения без лишних телодвижений.
UPD Благодаря наводке из комментариев, за что очень сильно благодарен комментаторам, я решил все-таки обратиться к мануалу, хотя с этого наверно и стоило начать. В мануале описания для инструкции возведения в квадрат нет, зато есть инструкция для возведения в степень. В блочном виде (Ladder Diagram) это блок XPY (X to the Power of Y), в структурном тексте (ST) как раз и есть та самая инструкция dest := sourceX ** sourceY; И на следующей странице мануала так и пишется If Source X is negative, Source Y must be an integer value or a minor fault will occur.
Комментарии (14)
Cerberuser
27.11.2022 08:31+8И в итоге получилось что Penetration ** 2.0 вызывает переполнение, а Penetration * Penetration нет.
Видимо, дело в том, что для отрицательных чисел определено возведение только в целую степень (с точки зрения контроллера), а 2.0 - число пусть и равное целому, но всё-таки с точки зрения производимых операций вещественное, и какой-то из шагов реализации к этому не приспособлен?
Weron2 Автор
27.11.2022 14:54+1Спасибо за наводку! Хочу попробовать потом сделать возведение в степень целого числа
Wesha
27.11.2022 08:32+2Тссссс! Молчите! Сейчас молодой человек откроет для себя дополнительное представление отрицательных чисел...
An_private
27.11.2022 11:26+7Как уже сказали - проблема в попытке возведения отрицательного числа в вещественную степень. Дело в том, что обычно она вычисляется по формуле a ^ x = e ^ (x * ln a) , а без перехода в комплексные числа натурального логарифма отрицательного числа не существует.
Функции sqr в этом ПЛК нет?
Weron2 Автор
27.11.2022 14:34+1Спасибо за формулу, теперь стало понятнее! Насчет sqr не в курсе, нужно попробовать. Попробую сделать эксперимент с sqr, и с целой 2, вместо вещественной 2.0
UPD похоже что в контроллере SQR инструкция используется для квадратного корня... Но главное сейчас принцип понятен, почему так, и это уже хорошо))
An_private
27.11.2022 14:38+3Только не перепутайте sqr и sqrt - одно из них возведение в квадрат, а второе - взятие квадратного корня
Weron2 Автор
27.11.2022 15:01+1Да, уже перепутал, блин) Исправил в комментарии. Но вопреки устоявшимся традициям, по мануалу именно SQR инструкция для квадратного корня. Но главное есть понимание почему так.
s_f1
27.11.2022 12:20+3потому что случай довольно интересный,
хотя он описан в школьном учебнике. Нехорошо прогуливать математику! ) А почему тут в показателе степени вообще используется вещественное вместо целого?Weron2 Автор
27.11.2022 14:39Насчет математике совсем зря, чисто математически нет никакой разницы как считать, а вот компьютеру в этом есть разница. Вещественное число решили использовать разработчики, я не в курсе почему так. Полагаю что просто тупо раз везде вещественные коэффициенты, то решили и степень так же записать. Опять же по мне куда проще и понятнее тупо умножение числа само на себя
Weron2 Автор
27.11.2022 15:22+5Благодаря наводке из комментариев, за что очень сильно благодарен комментаторам, я решил все-таки обратиться к мануалу, хотя с этого наверно и стоило начать. В мануале описания для инструкции возведения в квадрат нет, зато есть инструкция для возведения в степень. В блочном виде (Ladder Diagram) это блок XPY (X to the Power of Y), в структурном тексте (ST) как раз и есть та самая инструкция dest := sourceX ** sourceY; И на следующей странице мануала так и пишется If Source X is negative, Source Y must be an integer value or a minor fault will occur.
mortadella372
Вы не поверите.. Об этом даже кино сняли! И не одно..
Polaris99
Удивительно вообще, как у человека такая идея возникла, использовать вещественное число для банального возведения в квадрат.
Weron2 Автор
Это при том что проект сдан и находился в эксплуатации довольно давно. Я все же думаю что разработчик тупо везде щелкал вещественные числа без разбора степень это или просто коэффициент. Ну и мало того не задумываясь о том, что нельзя возводить отрицательные числа в действительную степень (я если честно и сам о таком нюансе не предполагал даже).