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
}
Данная диагностика классифицируется как:
|