On Which Platforms Does Integer Divide by Zero Trigger a Floating Point Exception

Why does integer division by zero result in a floating point exception?

Does integer division actually use the FPU under the hood?

No, Linux just generates SIGFPE in this case too (it's a legacy name whose usage has now been extended). Indeed, the Single Unix Specification defines SIGFPE as "Erroneous arithmetic operation".

Floating Point Exception - Division between integers

Linux maps the #DE (division by zero exception) generated by the CPU to the SIGFPE signal, which is then translated to the human-readable error message "Floating point exception"; unfortunately, it's quite misleading, as no floating point at all is involved into the process.

Now, given that rbx is 2, of course you are not dividing by zero. Still, there's another case when x86 generates a #DE exception: if the result is too big to fit into the target register.

In your code you are using the 64 bit form of the instruction (you wrote rbx - a 64 bit register - as divisor) which means that you are asking to divide rdx:rax (i.e. the 128 bit value obtained by joining rdx and rax) by rbx, and to put the result into rax (quotient) and rdx (remainder).

Since you are not zeroing out rdx, most probably it contains some big garbage value (residual from some previous computation?), and the division by two results in a quotient too big for rax. Hence, the #DE exception.

Long story short: zero out rdx before the div and everything will work out smoothly.

Floating point exception, but no division by zero. What is it?

Replace mov al, [i] with movzx ax, [i].

You are leaving at least 0x30 in ah which leads to ax >= 0x3000 that in turn prevents the result of the division between ax and 10 to fit in al.

ah is not zero because div bl sets it (it is the remainder of the division) but it is never cleared after the loop jumps back.

This also means that the first iteration works by accident: ah is initially zero because SYS_READ is 3 and thus the needed mov eax, 3 sets ah to zero before the loop is entered.

Also, you are not correctly setting the starting address of the string to print (it's always one byte less).

Why does this code get floating point exception when there is no float data-type?

It is perfectly normal for integer division to produce an exception that is reported as "floating point exception" on some platforms (Linux, for one example). You can easily get it from integer division by zero, or, for another example, by triggering overflow as in

int i = INT_MIN;
int b = -1;
i = i / b;

http://coliru.stacked-crooked.com/a/07c5fdf47278b696

In certain contexts this exception might appear or disappear depending on optimization levels. The exception is normally only triggered when the compiler decided to generate the actual division instruction (as opposed to optimizing out the division).


In your case unsigned integer division is used, so division by zero seems to be the only possible culprit. I would guess that this

unsigned long long int deno = pow(10,n-1);

happens to result in zero in deno. pow is a floating-point function that produces a floating-point result. Conversion from floating-point type to integer type leads to undefined behavior if the original value is too large (which is the case for n equal to 50). Note that this is the case even if the target integer type is unsigned.

Handling division by zero with <csignal> results in unexpected behaviour

It's defined as:

The SIGFPE signal reports a fatal arithmetic error. Although the name is derived from “floating-point exception”, this signal actually covers all arithmetic errors, including division by zero and overflow. If a program stores integer data in a location which is then used in a floating-point operation, this often causes an “invalid operation” exception, because the processor cannot recognize the data as a floating-point number.

There's no reason for it to be labelled specifically FPE but these sorts of labels can evolve in unpredictable ways. I wouldn't read too much into it.

These signals are part of the POSIX standard and may not be fully supported or implemented in Windows. The Windows implementation of these support facilities is lacking in a number of areas, like how fork() is unsupported.

Floating point exception without division by zero

What should I do to resolve this problem?

The first thing to address this problem would be to have a look at the hints your compiler is able to provide you. That is, enable warnings (-Wall) and debugging symbols (-g):

$ gcc test.c -Wall -g
test.c: In function ‘main’:
test.c:25:11: warning: ‘c’ may be used uninitialized in this function [-Wuninitialized]

There may be something wrong with the variable c in line 25, which is exactly the print statement:

printf("%d",(b%c));

Let's see what happens when we run it:

$ ./a.out
1
2
3
Floating point exception (core dumped)

Ah, well, it fails. But how did it crash? That's a use case for gdb:

$ gdb ./a.out
GNU gdb (GDB) Fedora (7.5.1-37.fc18)
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/moooeeeep/a.out...done.
(gdb) run
Starting program: /home/moooeeeep/a.out
1
2
3

Program received signal SIGFPE, Arithmetic exception.
0x0000000000400640 in main () at test.c:25
25 printf("%d",(b%c));
Missing separate debuginfos, use: debuginfo-install glibc-2.16-30.fc18.x86_64

Just at line 25. Suspicious. Let's examine the contents of the variables:

(gdb) print b
$1 = 1
(gdb) print c
$2 = 0

That variable c is indeed zero. But why is it causing a floating point exception when there are only integers? (Others have observed this before you.) As you can see in the debugger it's called an arithmetic exception (c.f.):

The SIGFPE signal reports a fatal arithmetic error. Although the name is derived from “floating-point exception”, this signal actually covers all arithmetic errors, including division by zero and overflow.

Let's see what valgrind tells us:

$ valgrind ./a.out
==3113== Memcheck, a memory error detector
==3113== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3113== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3113== Command: ./a.out
==3113==
1
2
3
==3113==
==3113== Process terminating with default action of signal 8 (SIGFPE)
==3113== Integer divide by zero at address 0x403E58A5B
==3113== at 0x400640: main (test.c:25)
==3113==
==3113== HEAP SUMMARY:
==3113== in use at exit: 0 bytes in 0 blocks
==3113== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3113==
==3113== All heap blocks were freed -- no leaks are possible
==3113==
==3113== For counts of detected and suppressed errors, rerun with: -v
==3113== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Floating point exception (core dumped)

valgrind identified an integer division by zero exactly at line 25. You should have an eye on that line (and the variables involved)!


Note that most IDEs (e.g., Eclipse) have (some of) these tools directly integrated, which makes debugging really a charm.



Related Topics



Leave a reply



Submit