Search code examples
cfloating-pointgdbprimesrealloc

Why do I get a floating-point exception on modulo operation after a call to realloc()?


I wrote this code to find the xth greatest prime numbers:

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) {}
}

However it returned a "Floating point exception" when using big numbers (> 8,000).

When happens here:

  • The user chooses a number.
  • maxNumber is set to the square root of the chosen number.
  • Firstly, a double pointer of size 1000 * sizeof(double) is allocated. It is stored in the primes variable.
  • If a number is found to be prime, it is added to the array represented by the pointer.
    • When the 1,000th number is added to the array, the primes pointer is reallocated to store 2,000 more numbers.

When I used gdb to find out the cause of the error, I found that this part was the cause of problem:

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

Update: I thought the first if statement would catch that issue, because printf("%f", primes[tested]) prints 0. However, it doesn't and the "break" is not executed.

When the code broke, tested was 1001. I convert primes[tested] to an integer because the modulo arithmetic operation I use requires ints to work. However, when I print primes[tested] from code it shows 0. If I print the value from gdb, I get 6.1501785659964211e-319.

What am I missing? Should I modify my call to realloc to avoid this exception?


Solution

  • I thought the first if statement would catch that issue, because printf("%f", primes[tested]) prints 0. However, it doesn't and the "break" is not executed.

    You test whether primes[tested] == 0, but your code is only valid if ((int)primes[tested]) == 0. These are not at all the same thing. Moreover, printing the value of primes[tested] with format %f does not reliably tell you differently, because it gives you only 6 digits after the decimal point. Try a "%e" format instead, and test the condition you actually require, not a related, weaker one.

    But even better, don't use a floating-point type here. FP has no business being used in a discrete math problem, such as you appear to be trying to solve. If primes[tested] in fact holds prime or possibly-prime numbers then unsigned long long int likely has the same size as double, and almost surely can exactly represent a wider range of primes. Or if it just contains flags, such as in a prime number sieve, then anything wider than char is wasteful.