Quem tem medo de divisão por zero?

Até pouco tempo precisavamos ser muito cuidadosos com divisões por zero. Ainda há motivos para tanto, por estranho que pareça, com divisões entre valores inteiros. Em ponto-flutuante o problema não é tão grande… Eis um teste:

/* fperr.c */
#include <stdio.h>

int intDiv(int a, int b) { return a / b; }
float floatDiv(float a, float b) { return a / b; }

int main(int argc, char *argv[])
{
  int rI;
  float rF;

  rF = floatDiv(1.0f, 0.0f);
  printf("%g\n", rF);
  rI = intDiv(1, 0);
  printf("%d\n", rI); /* Nunca chegará aqui! */

  return 0;
}

Ao compilar o programa e executá-lo, eis o que resulta:

$ ./fperr
inf
Floating point exception

Como assim “Floating point exception”? A divisão não foi feita entre dois inteiros? Será? Eis o código das duas funções intDivfloatDiv, na arquitetura x86_64:

intDiv:
  push  rbp 
  mov rbp, rsp
  mov DWORD PTR [rbp-4], edi
  mov DWORD PTR [rbp-8], esi
  mov eax, DWORD PTR [rbp-4]
  mov edx, eax
  sar edx, 31
  idiv  DWORD PTR [rbp-8]
  pop rbp
  ret

floatDiv:
  push  rbp #
  mov rbp, rsp
  movss DWORD PTR [rbp-4], xmm0
  movss DWORD PTR [rbp-8], xmm1
  movss xmm0, DWORD PTR [rbp-4]
  divss xmm0, DWORD PTR [rbp-8]
  movss DWORD PTR [rbp-12], xmm0
  mov eax, DWORD PTR [rbp-12]
  mov DWORD PTR [rbp-12], eax
  movss xmm0, DWORD PTR [rbp-12]
  pop rbp #
  ret

Não há dúvidas: intDiv realiza uma divisão inteira (com “idiv”) e floatDiv usa SSE para realizar a divisão (com “divss”). A discrepância ocorre porque os processadores modernos realizam multiplicação e divisão em ponto flutuante o tempo todo, mesmo com tipos integrais. O padrão C99 (ou será o IEEE 754?) define que divisões por zero, em ponto flutuante, retornan ±inf ou ±NaN (no caso de 0.0 / 0.0). Mas isso não pode ser usado nas divisões inteiras por que os tipos integrais não podem armazenar valores infinitos ou não-números!

Assim, a divisão por zero, usando inteiros, gera uma exceção e um abort(). A divisão por zero, em ponto-flutuante, não o faz! A melhor maneira de evitar a exceção por divisão por zero, inteira, ainda é verificar se o denominador é zero…

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s