# V2646. MISRA. All arguments of any multi-argument type-generic macros from <tgmath.h> should have the same type.

Диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.

Это правило актуально только для языка C.

Все аргументы, передаваемые в макросы из заголовочного файла `<tgmath.h>`, должны иметь одинаковый тип. Целочисленные аргументы после преобразования (integer promotion) также должны быть одного типа.

Правило применимо к следующим макросам: `atan2`, `copysign`, `fdim`, `fma`, `fmax`, `fmin`, `fmod`, `frexp`, `hypot`, `ldexp`, `nextafter`, `nexttoward`, `pow`, `remainder`, `remquo`, `scalbn`, `scalbln`.

Примечание. Последний аргумент макросов `frexp` и `remquo` используется для записи результата, его тип может отличаться от остальных.

На основе типов аргументов выбирается вызываемая функция и, соответственно, тип возвращаемого значения. В C нет механизма перегрузки функций, однако выбор подходящей альтернативы при вызове соответствующего макроса всё же может быть реализован в конкретном компиляторе. Например, в MSVC CL для этого используется механизм generic selection. Если аргументы имеют разные типы, может быть выбрана неправильная функция, что, в свою очередь, может привести к непредсказуемому или неточному результату.

Рассмотрим пример:

    #include <tgmath.h>

    void first()
    {
      float op1 = 15.61;
      float op2 = 31.4;
      float res = pow(op1, op2); //2.970643e+37
    }

    void second()
    {
      double op1 = 15.61;
      float op2 = 31.4;
      float res = pow(op1, op2); //2.970645e+37
    }

В приведённом коде аргументы макроса имеют типы разных размеров. В результате будут вызваны разные функции: `float powf(float, float)` в первом случае и `double pow(double, double)` — во втором. Логика выбора конкретной функции зависит от компилятора и не всегда прозрачна. К тому же, результат вычислений во втором примере будет неявно преобразован из типа `double` к типу `float`. Подобные преобразования в некоторых случаях могут привести к потере точности или немного разным результатам вычислений.

Избавиться от ошибки можно явно приведя аргументы к одинаковому типу. В таком случае после раскрытия макроса будет вызвана соответствующая функция и возвращаемое значение гарантированно будет иметь тип `float`, а результаты не будут отличаться.

Исправленный пример:

    #include <tgmath.h>

    void second()
    {
      double op1 = 15.61;
      float op2 = 31.4;
      float res = pow((float)op1, op2); //2.970643e+37
    }

Данная диагностика классифицируется как:

MISRA-C-2023-21.23