Почему я получаю исключение с плавающей точкой при операции по модулю после вызова realloc ()? - PullRequest
0 голосов
/ 24 июня 2019

Я написал этот код, чтобы найти x th наибольших простых чисел:

for (int i = 3 /* 2 has already been added to the list */; i < maxNumber; i += 2) {
  for (int tested = 0; ; tested++) {
    if (primes[tested] == 0) {
      break;
    }
    if (i % (int) primes[tested] == 0) {
      goto loop;
    }
  }

  count++;
  if (count == primesSize) {
    primesSize += 2000;
    primes = (double*) realloc(primes, sizeof(double) * primesSize);
  }

  primes[count - 1] = i;
  printf("Prime number #%d: %d\n", count, i);
  printf("Prime size: %d\n", primesSize);

  loop: /* statement that does nothing */ if (1) {}
}

Однако при использовании больших чисел (> 8 000) он вернул «исключение с плавающей запятой».

Когда здесь происходит:

  • Пользователь выбирает число.
  • maxNumber устанавливается в квадратный корень из выбранного числа.
  • Во-первых, выделяется двойной указатель размером 1000 * sizeof(double).Он сохраняется в переменной primes.
  • Если число найдено простым, оно добавляется в массив, представленный указателем.
    • Когда в массив добавляется 1000-е число, указатель primes перераспределяется для хранения еще 2000 номеров.

Когда яиспользовал gdb, чтобы выяснить причину ошибки, я обнаружил, что эта часть была причиной проблемы:

for (int tested = 0; ; tested++) {
  if (primes[tested] == 0) {
    break;
  }
  if (i % (int) primes[tested] == 0 /* breaks here */) {
    goto loop;
  }
}

Обновление: Я думал, что первый оператор if будетперехватить эту проблему, потому что printf("%f", primes[tested]) печатает 0. Однако это не так, и "разрыв" не выполняется.

Когда код сломался, tested был 1001. Я преобразую primes[tested] вцелое число, потому что для использования по модулю арифметической операции , которую я использую, требуются целые числа.Однако, когда я печатаю primes[tested] из кода, он показывает 0 .Если я распечатываю значение из gdb, я получаю 6.1501785659964211e-319 .

Чего мне не хватает?Должен ли я изменить свой вызов на realloc, чтобы избежать этого исключения?

Ответы [ 3 ]

3 голосов
/ 24 июня 2019

Я думал, что первый оператор if уловит эту проблему, потому что printf("%f", primes[tested]) печатает 0. Однако это не так, и "break" не выполняется.

Вы проверяете, есть ли primes[tested] == 0, но ваш код действителен только если ((int)primes[tested]) == 0.Это совсем не одно и то же.Кроме того, печать значения primes[tested] в формате %f не дает надежного подтверждения, поскольку дает только 6 цифр после десятичной точки.Вместо этого попробуйте формат "%e" и протестируйте условие, которое вам действительно нужно, а не связанное, более слабое.

Но, что еще лучше, не используйте здесь тип с плавающей точкой.У FP нет бизнеса, используемого в дискретной математической задаче, которую вы, похоже, пытаетесь решить.Если primes[tested] на самом деле содержит простые или, возможно, простые числа, то unsigned long long int, вероятно, имеет тот же размер, что и double, и почти наверняка может точно представлять более широкий диапазон простых чисел.Или, если он просто содержит флаги, такие как сито с простым числом, тогда все, что шире, чем char, является расточительным.

2 голосов
/ 24 июня 2019

Числа с плавающей запятой, которые действительно близки к нулю, все еще не точно равны нулю. Таким образом, ваша проверка на равный нулю не удалась.

Обратите внимание, что если вам не повезло с типом машины, которую вы компилируете даже на

double f = 1.1;
double x = f;
double y = x;

if( y == f )
    puts("This is not always true!");

Математика с плавающей запятой на компьютерах сложна и не работает, как вы ожидаете от написания математики по математике, где x равно y равно f по определению. Нет, компьютер с плавающей запятой работает на битовых шаблонах, и они должны быть точно такими же .

В любом случае, чтобы ответить на ваш вопрос. Используйте точно такой же int для вашего оператора if, как и в модуле, и он должен работать.

А также новая память, возвращаемая с realloc, не будет автоматически обнуляться.

А также третий: если вам пришлось приводить результат из realloc с (double*), значит, вы находитесь в компиляторе C ++ и должны использовать std::vector<double>. Это намного лучше на самом деле. В противном случае, если вы пишете код C, тогда напишите код C .

0 голосов
/ 24 июня 2019

Я подумал, что первое выражение if уловит эту проблему, потому что printf ("% f", простые числа [проверено]) печатает 0.

Если это утверждение печати дает правильный вывод в соответствии с тем, что вы сказали, тогда, очевидно, Floating point exception имеет смысл.

Вы думаете, что если оператор должен обрабатывать исключение, но это на самом деле не выполняется так, как вы думаете. Сначала он выполняет часть i % (int) primes[tested], затем сравнивает результат с 0. Таким образом, очевидно, что исключение происходит еще до того, как может работать. Надеюсь, вы понимаете.

К вашему сведению, если b = 0, то a % b вызывает исключение с плавающей запятой.

И если у вас больше сомнений относительно шагов выполнения оператора if, запустите этот код самостоятельно:

if (printf("Hello") == 5) {
    printf(" World");
}

Затем попытайтесь понять, какой printf() выполняется первым.

...