Floating Point Exception (Core Dumped) While Doing Division in Assembly

x86 Assembly (NASM): Floating Point Exception, not dividing by 0?

I see 2 problems with your program.

  • The div ebx instruction uses EDX:EAX as the dividend and you fail to set it up. Just insert an xor edx,edx

    xor   edx, edx ; <--------------------------------- Add this !
    div ebx
    mov ebx, eax ; ebx = (A + B) / (A - B)
  • After the division you store the quotient in EBX but you never pick it up again to display the result!

    mov   ecx, [B]
    mov eax, [A]
    mul ecx
    mov ecx, eax ; ecx = A * B
    mov eax, ebx ; <--------------------------------- Add this !
    add eax, ecx ; eax = A * B + (A + B) / (A - B)

The 2nd issue can be solved in a shorter way:

mov   ecx, [B]
mov eax, [A]
mul ecx
add eax, ebx ; eax = A * B + (A + B) / (A - B)

EDIT (late catch, sorry)

I check for division by 0.

A * B + ( A + B ) / ( A - B )

You've based your check on the divider being (A - B) and thus exit if A is equal to B.

Correct, but the program code erroneously calculates (A - B) / (A + B) and so is using (A + B) as the divider!

This is my version of calculating A * B + ( A + B ) / ( A - B ):

mov     ebx, [A]
sub ebx, [B] ; EBX = (A - B)
jz error ; Guard against division by zero!

mov eax, [A]
add eax, [B] ; EAX = (A + B)

xor edx, edx ; EDX:EAX = (A + B)
div ebx ; EAX = (A + B) / (A - B)

mov ebx, [A]
imul ebx, [B] ; EBX = A * B

add eax, ebx ; EAX = A * B + (A + B) / (A - B)

Getting floating point exception while trying to use div in assembly

The div instruction divides the two-word parameter dx/ax by the operand. If the quotient is too large to fit into a word, it will throw that exception.

Reference: http://siyobik.info.gf/main/reference/instruction/DIV

What do you have in the dx register? Most likely dx/ax divided by 15 does not fit in a 16-bit word.

Assembly x86-64 idivq - floating point exception

idivq %rcx will divide a 128-bit numerator, whose high half is in %rdx and whose low half is in %rax, by the 64-bit denominator in %rcx. See the description of IDIV.

Your %rdx seems to contain your 64-bit numerator and you haven't initialized %rax at all. So you are dividing some 128-bit garbage number by whatever's in %rcx. If the result doesn't fit in 64 bits, you get a divide overflow exception, which Unix OSes typically handle by delivering SIGFPE, "Floating point exception", although no floating point is involved. (There isn't a specific signal for integer divide overflow in Unix, and so they adopted this one as the closest fit.)

If your numerator is only 64 bits, load it into %rax and execute cqto (called CQO in Intel's naming) to sign-extend it into %rdx. Then idivq %rcx will divide it by the 64-bit denominator in %rcx. Note that overflow is only possible in this case if you divide the most negative signed 64-bit integer (0x8000000000000000) by -1, or if you divide by zero.

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 IDIV with -1 causes floating point exception?

Note that I know I need to sign extend edx:eax ...

If you don't sign-extend eax, edx:eax is interpreted as 64-bit signed number:

In your case, this would be 0x00000000fffffffb which is 4294967291 (and not -5).

div and idiv will cause an exception in two cases:

  • You divide by zero
  • The division result is not in the range that can be represented by the eax register

eax can hold signed numbers in the range from -2147483648 to +2147483647, but -4294967291 lies outside that range. You'll get an exception.

should not cause an FPE.

Indeed, div and idiv will cause an "integer division exception", not "a floating-point exception".

However, many OSs will show the message "floating point exception"; POSIX defines SIGFPE as covering any arithmetic exception.

Calling printf() in assembly causes a 'floating point exception'

Take another look at the x86-64 ABI calling conventions. Specifically, on page 15:

Registers %rbp, %rbx and
%r12 through %r15 “belong” to the calling function and the called function is
required to preserve their values. In other words, a called function must preserve
these registers’ values for its caller. Remaining registers “belong” to the called
function. If a calling function wants to preserve such a register value across a
function call, it must save the value in its local stack frame.

So when you call printf, you must assume that all registers except rbp, rbx, r12..r15 are clobbered. This has two effects for your program:

  • You attempt to save ax around the call to printf by stashing its value in the si register and then putting it back later, but this doesn't help because si can be clobbered.

  • Your div cx instruction divides the contents of dx:ax by cx, but dx might also have been clobbered.

The latter is the specific cause of the SIGFPE (which despite its name is also raised on an integer divide overflow), at least in my test runs. After the printf returns, dx contains some huge number, such that dx:ax divided by cx does not fit in 16 bits, which is an overflow.

(This also explains why the crash went away when you took out the printf call - it was no longer there to clobber your registers.)

The other lesson here is that you can't check for divide overflow on x86 by doing a jo afterwards; this error is primarily signaled by an exception, not by flags. So you really have to check your operands before executing the instruction, or else arrange to handle the exception if it occurs (which is more complicated and beyond the scope of this answer).

A couple of other notes:

  • Before your final call to printf (just before jmp exit), you don't load anything into rsi, so the value printed is garbage.

  • 16-bit arithmetic is usually not to be preferred on x86-32 or x86-64 unless there is a really good reason for it. It is no faster and bloats your code with operand size prefixes. Better to do all your work with 32-bit arithmetic.

  • Since you're using your own entry point rather than letting the C library call your main, this means that libc has not had the opportunity to run its own initialization code. Therefore, it is not necessarily safe to call any libc function, particularly stdio and allocation functions. It seems that printf happens to work okay in this case, and maybe it is all right for debugging, but you shouldn't plan to write your program this way for production.

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".



Related Topics



Leave a reply



Submit