exponenta event banner

Прецизионные потери в целочисленном преобразовании в преобразование с плавающей запятой

Наименьшие значащие биты целого числа, потерянные при преобразовании в тип с плавающей запятой

Описание

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

Например, long int стоимость 1234567890L слишком велик для переменной типа float .

Риск

Если тип с плавающей запятой не может представлять целое значение, поведение не определено (см. C11 standard, 6.3.1.4, para.2). Например, наименьшие значащие биты значения переменной могут быть отброшены, что приводит к неожиданным результатам.

Зафиксировать

Преобразование в тип с плавающей запятой, который может представлять целое значение.

Например, если float тип данных не может представлять целое значение, используйте double вместо этого тип данных.

При записи функции, которая преобразует целое число в тип с плавающей запятой, перед преобразованием проверьте, может ли целочисленное значение быть представлено в типе с плавающей запятой. Например, DBL_MANT_DIG * log2(FLT_RADIX) представляет количество базовых-2 цифр в типе double. Перед преобразованием в тип double, убедитесь, что это число больше или равно точности целого числа, которое вы преобразуете. Определение точности целого числа num, используйте следующий код:

 size_t precision = 0;
 while (num != 0) {
    if (num % 2 == 1) {
      precision++;
    }
    num >>= 1;
 }

Некоторые реализации предоставляют функцию builtin для определения точности целого числа. Например, GCC предоставляет функцию __builtin_popcount.

Примеры

развернуть все

#include <stdio.h>

int main(void) {
  long int big = 1234567890L;
  float approx = big;
  printf("%ld\n", (big - (long int)approx));
  return 0;
}

В этом коде C long int переменная big преобразуется в float.

Коррекция - использование более широкого типа с плавающей запятой

Одной из возможных корректировок является преобразование в double тип данных вместо float.

#include <stdio.h>

int main(void) {
  long int big = 1234567890L;
  double approx = big;
  printf("%ld\n", (big - (long int)approx));
  return 0;
}

Информация о результатах

Группа: Числовые
Язык: C | C++
По умолчанию: Откл.
Синтаксис командной строки: INT_TO_FLOAT_PRECISION_LOSS
Воздействие: Низкий
CWE ID: 189, 681, 704
Представлен в R2018b