简体   繁体   中英

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

I wrote this code to find the x th 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?

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.

Floating point numbers which are really close to zero are still not exactly zero. So your check for equals zero fails.

Note that if you get unlucky enough on the type of machine you compile on even

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

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

Floating point math on computers is tricky and doesn't work as you'd expect from writing math in mathematics where x equals y equals f by definition. No, computer floating point works on bit patterns and they have to be exactly the same .

Anyway, to answer your question. Use the exact same int cast on your if statement as in your modulus and it should work.

And also the new memory returned from realloc will not automatically be set to zero.

And also the third: If you had to cast the return from realloc with (double*) then you're in a C++ compiler and should be using std::vector<double> . It is much better really. Otherwise if you're writing C code then write C code .

I thought the first if statement would catch that issue, because printf("%f", primes[tested]) prints 0.

If this print statement is giving right output according to what you have said, then obviously the Floating point exception makes sense.

You are thinking that if statement should handle the exception, but that really doesn't execute like the way you are thinking. At first it executes the i % (int) primes[tested] part then it compares the result with 0 . So, obviously the exception occurs even before the if could function. Hope you understand.

FYI, If b = 0 , then a % b causes floating point exception.

And if you have more doubt on the execution steps of the if statement, then run this code yourself:

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

Then try to understand which printf() executes first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM