странное поведение преобразования double в int в c ++ - PullRequest
3 голосов
/ 07 декабря 2009

Следующая программа показывает странное поведение преобразования double в int, которое я вижу в c ++:

#include <stdlib.h>
#include <stdio.h>

int main() {
  double d = 33222.221;
  printf("d = %9.9g\n",d);

  d *= 1000;
  int i = (int)d;

  printf("d = %9.9g | i = %d\n",d,i);

  return 0;
}

Когда я компилирую и запускаю программу, я вижу:

g++ test.cpp
./a.out
d = 33222.221
d =  33222221 | i = 33222220

Почему я не равен 33222221? Версия компилятора GCC 4.3.0

Ответы [ 5 ]

10 голосов
/ 07 декабря 2009

Представление с плавающей точкой почти никогда не бывает точным (только в особых случаях). Каждый программист должен прочитать это: Что должен знать каждый учёный об арифметике с плавающей точкой

Короче говоря - ваш номер, вероятно, 33222220.99999999999999999999999999999999999999999999999999999999999999998 (или что-то в этом роде), который становится 33222220 после усечения.

1 голос
/ 07 декабря 2009

Когда вы присоедините отладчик и осмотрите значения, вы увидите, что значение d на самом деле 33222220.999999996, которое корректно усекается до 33222220 при преобразовании в целое число.

Существует конечное количество чисел, которые можно сохранить в двойной переменной, и 33222221 не является одним из них.

0 голосов
/ 07 декабря 2009

Я не хочу повторять объяснения других комментариев.

Итак, вот лишь совет, чтобы избежать проблем, подобных описанной:

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

  2. Если арифметика с плавающей точкой действительно необходима, вы не должны сравнивать числа оператором == во всех отношениях! Вместо этого используйте свою собственную функцию сравнения (или используйте функцию, предоставленную какой-то библиотекой), которая выполняет что-то вроде сравнения «почти равно» с использованием некоторого сравнения эпсилонов (либо абсолютного, либо относительно увеличения числа).

См., Например, отличную статью

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

вместо Брюса Доусона!

Stefan

0 голосов
/ 07 декабря 2009

Если вы измените «9,9 г» в своих printf() вызовах на 17,17, чтобы восстановить все возможные цифры точности с 64-битным номером IEEE 754 FP, вы получите 33222220,99999996 для двойного значения. Преобразование int имеет смысл.

0 голосов
/ 07 декабря 2009

Из-за приближения с плавающей точкой 33222.221 может фактически быть 33222.220999999999999. Умножается на 1000 и дает 33222220,9999999999999. Приведение к целому числу игнорирует все десятичные дроби (округление вниз) для конечного результата 33222220.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...