Недавно на работе столкнулись с интересной ситуацией, о которой захотелось написать тут, потому что случай довольно интересный, хотя как и оказалось простой. На одном из агрегатов, управляемым контроллером от 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)


  1. mortadella372
    27.11.2022 04:41
    +7

    Penetration ** 2.0 вызывает переполнение

    Вы не поверите.. Об этом даже кино сняли! И не одно..


    1. Polaris99
      27.11.2022 20:22

      Удивительно вообще, как у человека такая идея возникла, использовать вещественное число для банального возведения в квадрат.


      1. Weron2 Автор
        27.11.2022 21:07
        +1

        Это при том что проект сдан и находился в эксплуатации довольно давно. Я все же думаю что разработчик тупо везде щелкал вещественные числа без разбора степень это или просто коэффициент. Ну и мало того не задумываясь о том, что нельзя возводить отрицательные числа в действительную степень (я если честно и сам о таком нюансе не предполагал даже).


  1. Cerberuser
    27.11.2022 08:31
    +8

    И в итоге получилось что Penetration ** 2.0 вызывает переполнение, а Penetration * Penetration нет.

    Видимо, дело в том, что для отрицательных чисел определено возведение только в целую степень (с точки зрения контроллера), а 2.0 - число пусть и равное целому, но всё-таки с точки зрения производимых операций вещественное, и какой-то из шагов реализации к этому не приспособлен?


    1. Weron2 Автор
      27.11.2022 14:54
      +1

      Спасибо за наводку! Хочу попробовать потом сделать возведение в степень целого числа


  1. Wesha
    27.11.2022 08:32
    +2

    Тссссс! Молчите! Сейчас молодой человек откроет для себя дополнительное представление отрицательных чисел...


  1. An_private
    27.11.2022 11:26
    +7

    Как уже сказали - проблема в попытке возведения отрицательного числа в вещественную степень. Дело в том, что обычно она вычисляется по формуле a ^ x = e ^ (x * ln a) , а без перехода в комплексные числа натурального логарифма отрицательного числа не существует.

    Функции sqr в этом ПЛК нет?


    1. Weron2 Автор
      27.11.2022 14:34
      +1

      Спасибо за формулу, теперь стало понятнее! Насчет sqr не в курсе, нужно попробовать. Попробую сделать эксперимент с sqr, и с целой 2, вместо вещественной 2.0

      UPD похоже что в контроллере SQR инструкция используется для квадратного корня... Но главное сейчас принцип понятен, почему так, и это уже хорошо))


      1. An_private
        27.11.2022 14:38
        +3

        Только не перепутайте sqr и sqrt - одно из них возведение в квадрат, а второе - взятие квадратного корня


        1. Weron2 Автор
          27.11.2022 15:01
          +1

          Да, уже перепутал, блин) Исправил в комментарии. Но вопреки устоявшимся традициям, по мануалу именно SQR инструкция для квадратного корня. Но главное есть понимание почему так.


  1. s_f1
    27.11.2022 12:20
    +3

    потому что случай довольно интересный,
    хотя он описан в школьном учебнике. Нехорошо прогуливать математику! ) А почему тут в показателе степени вообще используется вещественное вместо целого?


    1. Weron2 Автор
      27.11.2022 14:39

      Насчет математике совсем зря, чисто математически нет никакой разницы как считать, а вот компьютеру в этом есть разница. Вещественное число решили использовать разработчики, я не в курсе почему так. Полагаю что просто тупо раз везде вещественные коэффициенты, то решили и степень так же записать. Опять же по мне куда проще и понятнее тупо умножение числа само на себя


      1. s_f1
        27.11.2022 20:21
        +4

        Чисто математически – в школьном учебнике прямо написано, что нельзя возводить отрицательное число в действительную степень. Вот здесь, например, доступно объясняют.


  1. 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.