Por que a divisão inteira resulta em um inteiro?

4

Aprendemos no Intro to Programming que, se você dividir dois inteiros, sempre obterá um inteiro. Para corrigir o problema, faça pelo menos um desses números inteiros em float.

Por que o compilador não entende que eu quero que o resultado seja um número decimal?

    
por moonman239 21.01.2016 / 21:18
fonte

4 respostas

13

Why doesn't the compiler understand I want the result to be a decimal number?

O compilador C ++ está simplesmente seguindo regras bem definidas e determinísticas conforme estabelecido no padrão C ++ . O padrão C ++ tem essas regras porque o comitê de padrões decidiu fazer dessa forma.

Eles poderiam ter escrito o padrão para dizer que números inteiros resultam em números de ponto flutuante, ou o fazem apenas no caso de um resto. No entanto, isso adiciona complexidade: Eu preciso saber antecipadamente qual é o resultado, ou talvez converter de volta para inteiro se ele sempre der um float. Talvez eu queira um inteiro.

Uma das principais filosofias do C ++ é "você não paga pelo que não usa". Se você realmente quiser a complexidade de misturar inteiros e flutuantes (e as instruções extras de CPU e acessos à memória que isso implica 1 ), faça o tipo convertido como você mencionou na sua pergunta . Caso contrário, atenha-se à matemática inteira padrão.

Por fim, misturar variáveis integrais e de ponto flutuante pode resultar em perda de precisão e, às vezes, em resultados incorretos, conforme discutido abaixo. Se você quer isto, então pague por ele: caso contrário, o padrão determina que os compiladores se atenham a um conjunto estrito de regras para misturar tipos de dados. Este é um comportamento bem definido: como um desenvolvedor de C ++, posso procurar isso no padrão e ver como ele funciona.

Existem essencialmente três maneiras de fazer o que você está tentando fazer, cada uma com benefícios e desvantagens.

  • Integer matemática: isso resulta no truncamento dos resultados durante a divisão conforme você descobriu. Se você quiser a parte decimal, você precisa tratar isso separadamente dividindo, obtendo o restante e tratando a parte decimal como o restante dividido pelo divisor. Isso é um pouco mais complexo de uma operação e tem mais variáveis para manipular.

  • Matemática de ponto flutuante: isso geralmente produz resultados corretos (suficientes) para valores pequenos, mas pode facilmente introduzir erros com precisão e arredondamento, especialmente à medida que o expoente aumenta. Se você dividir um número grande por um número pequeno, você pode até mesmo causar um estouro negativo ou simplesmente obter um resultado errado, porque as escalas dos números não funcionam bem entre si.

  • Faça sua própria matemática. Existem classes que lidam com precisão estendida de números decimais e racionais . Normalmente, eles são mais lentos que os tipos incorporados, mas geralmente ainda são bastante rápidos e fornecem cálculos de precisão arbitrária. O arredondamento e outros problemas não são automáticos como os do IEEE, mas você ganha mais controle e certamente mais precisão.

A chave aqui é escolher com base no domínio do problema. Todos os três métodos de representação de números têm seus próprios benefícios e desvantagens. Usando um contador de loop? Escolha um tipo integral. Representando locais no espaço 3D? Provavelmente um grupo de carros alegóricos. Quer rastrear dinheiro? Use um tipo decimal fixo.

1 Arquiteturas de CPU mais populares (por exemplo, x86-64 ) terão conjuntos de instruções que operam em diferentes tipos de registradores, como número inteiro e ponto flutuante, além de instruções extras para converter entre integral, ponto flutuante e várias representações deles (assinadas e não assinadas, flutuantes e duplas). Algumas dessas operações também podem implicar acesso à memória: converter um valor e armazená-lo na memória (sua variável). Matemática no nível da CPU não é tão simples quanto "integer in, float out". Enquanto adicionar dois inteiros pode ser uma operação muito simples, possivelmente uma única instrução, misturar tipos de dados pode aumentar a complexidade.

    
por 21.01.2016 / 21:41
fonte
7

Isso se deve à evolução do hardware. Nos primórdios dos computadores, nem todas as máquinas tinham uma unidade de ponto flutuante, o hardware simplesmente não conseguia entender a noção de um número de ponto flutuante. É claro, números de ponto flutuante podem ser implementados como uma abstração de software, mas isso tem desvantagens significativas. Toda a aritmética nessas máquinas tinha que ser aritmética inteira pura por padrão.

E ainda hoje, há uma firme distinção entre unidades aritméticas inteiras e de ponto flutuante dentro de uma CPU. Seus operandos são armazenados em arquivos de registradores separados para iniciar, e uma unidade inteira é conectada para obter dois argumentos inteiros e produzir um resultado inteiro que termina em um registrador inteiro. Algumas CPUs até exigem que um valor inteiro seja armazenado na memória e, em seguida, recarregadas de volta em um registrador de ponto flutuante, antes de poder ser recodificado em um número de ponto flutuante, antes que você possa executar uma divisão de ponto flutuante.

Como tal, a decisão tomada pelos desenvolvedores de C no início da linguagem (C ++ simplesmente herdou esse comportamento), foi a única decisão apropriada a ser tomada, e continua valendo hoje: Se você precisar de matemática de ponto flutuante, você pode usar isso. Se você não precisa, você não precisa.

    
por 21.01.2016 / 21:44
fonte
1

10/2 com inteiros dá exatamente 5 - a resposta correta.

Com a matemática de ponto flutuante, 10/2 pode dar a resposta correta *.

Em outras palavras, é impossível que os números de ponto flutuante sejam "perfeitos" no hardware atual - apenas a matemática inteira pode estar correta, infelizmente não é possível fazer casas decimais, mas há um trabalho fácil.

Por exemplo, em vez de 4/3, faça (4 * 1000) / (3 * 1000) == 1333. Basta desenhar a. no software ao exibir a resposta ao seu usuário (1.333). Isso fornece uma resposta precisa, em vez de uma incorreta em algumas casas decimais.

Erros matemáticos de ponto flutuante podem ser adicionados para causar erros significativos - qualquer coisa importante (como finanças) usará matemática inteira.

* o exemplo 10/2 estará correto com a matemática de ponto flutuante, mas você não pode confiar nele, muitos outros números dão resultados incorretos ... para mais detalhes, leia: link O ponto é que você não pode confiar na precisão sempre que pontos flutuantes estiverem envolvidos

    
por 06.03.2016 / 12:53
fonte
0
Embora tecnicamente não seja completamente correto, o C ++ ainda é considerado um superconjunto de C, foi inspirado por ele e, como tal, se apropria de algumas de suas propriedades, sendo a divisão inteira uma delas.

O C foi projetado principalmente para ser eficiente e rápido, e os inteiros são geralmente muito mais rápidos do que os pontos flutuantes, porque o tipo inteiro está ligado ao hardware, enquanto os pontos flutuantes precisam ser calculados.

Quando o operando / recebe dois inteiros, um do lado esquerdo e outro do lado direito, ele pode nem mesmo fazer divisão, o resultado pode ser computado usando adição simples e um loop, perguntando quantas vezes o operando do lado direito se encaixa no operando à esquerda.

    
por 21.01.2016 / 22:04
fonte

Tags