Por que a divisão inteira em C # retorna um número inteiro e não um número flutuante?

131

Alguém sabe por que a divisão inteira em C # retorna um número inteiro e não um float? Qual é a ideia por trás disso? (É apenas um legado de C / C ++?)

Em c #:

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

O resultado desse código seria:

'Hello world'

A rigor, não existe divisão inteira (divisão por definição é uma operação que produz um número racional, os números inteiros são um subconjunto muito pequeno).

BanditoBunny
fonte
46
porque é integerdivisão, não floating pointdivisão.
Hunter McMillen
ele precisa (no VB.Net) ser implementado de maneira diferente de uma maneira matemática natural, onde todo o resultado da operação de divisão é um número irracional.
precisa saber é o seguinte
3
Eu acho que você quer dizer números racionais . Consulte a Wikipedia : dividir dois números inteiros pode resultar em um restante. Para concluir a divisão do restante, o sistema numérico é estendido para incluir frações ou números racionais, como são geralmente chamados.
crashmstr
6
Esta é a razão pela qual eu não sou fã de "copiar sintaxe" nos idiomas. Eu venho do VB pensando que "C # é .NET" e não "C # é como C". Meu erro, eu acho, mas neste caso eu prefiro o modo VB. Se eles tiveram o problema de gerar um erro de compilador ao usar tipos simples não inicializados (você nem recebe um aviso em C), por que não avisá-lo quando atribui uma divisão inteira a um float?
Dard # 03

Respostas:

96

Embora seja comum o novo programador cometer esse erro de executar a divisão inteira quando na verdade pretendia usar a divisão de ponto flutuante, na prática real a divisão inteira é uma operação muito comum. Se você está assumindo que as pessoas raramente o usam e que toda vez que faz uma divisão, sempre precisará se lembrar de lançar pontos flutuantes, está enganado.

Primeiro, a divisão inteira é um pouco mais rápida; portanto, se você precisar apenas de um resultado com um número inteiro, é recomendável usar o algoritmo mais eficiente.

Em segundo lugar, existem vários algoritmos que usam divisão inteira e, se o resultado da divisão sempre foi um número de ponto flutuante, você seria forçado a arredondar o resultado toda vez. Um exemplo em cima da minha cabeça é mudar a base de um número. O cálculo de cada dígito envolve a divisão inteira de um número junto com o restante, em vez da divisão de ponto flutuante do número.

Devido a esses (e outros motivos relacionados), a divisão inteira resulta em um inteiro. Se você deseja obter a divisão de ponto flutuante de dois números inteiros, basta lembrar de converter um em double/ float/ decimal.

Servy
fonte
5
No VB.Net .Net, os arquitetos tomaram outra decisão: / - sempre uma divisão flutuante, \ - divisão inteira, portanto é meio inconsistente, exceto se você considerar o legado do C ++;
precisa saber é o seguinte
4
Você pode determinar, em tempo de compilação, se o /operador executará divisão de número inteiro ou de ponto flutuante (a menos que você esteja usando dinâmico). Se é difícil para você descobrir isso, porque você está fazendo tanto em que uma linha, então eu sugiro que quebrar essa linha-se em várias linhas de modo que é mais fácil de descobrir se os operandos são inteiros ou tipos de ponto flutuante. Os futuros leitores do seu código provavelmente o apreciarão.
Servy
5
Pessoalmente, acho problemático que eu sempre tenha que pensar quais são as variáveis ​​que estou dividindo, considero isso como um desperdício de minha atenção.
BanditoBunny
8
@pelesl Como seria uma enorme mudança para fazer aquilo em que um número astronômico de programas seria quebrado, posso dizer com total confiança que isso nunca acontecerá em C #. Esse é o tipo de coisa que precisa ser feita desde o primeiro dia em um idioma ou não.
Servy
2
@ Service: Existem muitas coisas assim em C, C ++ e C #. Pessoalmente, acho que o C # seria uma linguagem melhor se houvesse um operador diferente para a divisão inteira e, para evitar que um código legítimo produzisse um comportamento surpreendente, o int/intoperador era simplesmente ilegal [com um diagnóstico especificando que o código deve converter um operando ou usar o método outro operador, dependendo do comportamento desejado]. Se houvesse alguma outra boa sequência de tokens disponível para a divisão de números inteiros, talvez fosse possível descontinuar o uso /dessa finalidade, mas não sei o que seria prático.
precisa
77

Consulte a especificação C # . Existem três tipos de operadores de divisão

  • Divisão inteira
  • Divisão de ponto flutuante
  • Divisão decimal

No seu caso, temos a divisão Inteiro, com as seguintes regras aplicadas:

A divisão arredonda o resultado para zero e o valor absoluto do resultado é o maior número inteiro possível que é menor que o valor absoluto do quociente dos dois operandos. O resultado é zero ou positivo quando os dois operandos têm o mesmo sinal e zero ou negativo quando os dois operandos têm sinais opostos.

Eu acho que a razão pela qual o C # usa esse tipo de divisão para números inteiros (algumas línguas retornam resultado flutuante) é o hardware - a divisão de números inteiros é mais rápida e mais simples.

Sergey Berezovskiy
fonte
Quais idiomas retornam resultado flutuante? @SergeyBerezovskiy
Ilaria
40

Cada tipo de dados é capaz de sobrecarregar cada operador. Se o numerador e o denominador forem números inteiros, o tipo inteiro executará a operação de divisão e retornará um tipo inteiro. Se você deseja divisão de ponto flutuante, deve converter um ou mais números para tipos de ponto flutuante antes de dividi-los. Por exemplo:

int x = 13;
int y = 4;
float x = (float)y / (float)z;

ou, se você estiver usando literais:

float x = 13f / 4f;

Lembre-se de que os pontos flutuantes não são precisos. Se você se preocupa com precisão, use algo como o tipo decimal.

Steven Doggart
fonte
1
+1 por mencionar que apenas um termo precisa ser flutuado para fazer a divisão de ponto flutuante.
Xynariz
Obviamente, sua afirmação sobre precisão é correta no contexto da aprendizagem e em torná-la não muito complicada de entender. Como precisamos ser o mais precisos possível em nossos trabalhos, ainda quero esclarecer a precisão: de acordo com a IEE 754-1985, você PODE obter um resultado exato (mesmo que esse não seja o caso). Você pode obter um resultado preciso, quando os valores de cálculo foram apresentados exatamente antes e o resultado é - simplesmente falando - uma soma de potências de 2. Mesmo que não seja uma prática recomendada confiar nessa precisão nesses casos especiais.
L.L. Monty
Adição de precisão: a possibilidade de obter um resultado exato melhora drasticamente, pois o resultado é próximo de 1 ou -1. Pode ser um pouco confuso que essa propabilidade ainda permaneça 0, pois há números infinitos e um número finito de resultados, que podem ser apresentados exatamente. :)
L. Monty
1
@ L.Monty obrigado por trazer isso à tona. Aprendi mais sobre pontos flutuantes desde que escrevi esta resposta e o que você está dizendo é justo. Tecnicamente, eu ainda diria que minha afirmação “pontos flutuantes não são precisos” é aceitável, no sentido de que apenas porque algo pode ser preciso às vezes não significa que, como um todo, seja preciso. Como se costuma dizer, um relógio quebrado está certo duas vezes por dia, mas eu nunca chamaria um de instrumento de precisão. Na verdade, estou bastante surpreso com a parte que o incomodou mais do que minha sugestão de que o tipo decimal é preciso.
Steven Doggart
Os decimais são imprecisos, pelas mesmas razões que os carros alegóricos; é que os carros alegóricos são de base 2 e os decimais são de base 10. Por exemplo, um tipo decimal não pode conter com precisão o valor exato de 1/3.
Steven Doggart
11

Como você não usa nenhum sufixo, os literais 13e 4são interpretados como número inteiro:

Manual :

Se o literal tem nenhum sufixo, tem o primeiro desses tipos em que o seu valor pode ser representados: int, uint, long, ulong.

Assim, como você declara 13como inteiro, a divisão inteira será realizada:

Manual :

Para uma operação no formato x / y, a resolução de sobrecarga do operador binário é aplicada para selecionar uma implementação específica do operador. Os operandos são convertidos nos tipos de parâmetro do operador selecionado e o tipo do resultado é o tipo de retorno do operador.

Os operadores de divisão predefinidos estão listados abaixo. Todos os operadores calculam o quociente de x e y.

Divisão inteira:

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

E assim ocorre o arredondamento:

A divisão arredonda o resultado para zero e o valor absoluto do resultado é o maior número inteiro possível que é menor que o valor absoluto do quociente dos dois operandos. O resultado é zero ou positivo quando os dois operandos têm o mesmo sinal e zero ou negativo quando os dois operandos têm sinais opostos.

Se você fizer o seguinte:

int x = 13f / 4f;

Você receberá um erro do compilador, pois uma divisão de ponto flutuante (o /operador de 13f) resulta em um ponto flutuante, que não pode ser convertido para int implicitamente.

Se você deseja que a divisão seja uma divisão de ponto flutuante, terá que transformar o resultado em flutuante:

float x = 13 / 4;

Observe que você ainda dividirá números inteiros, que serão implicitamente convertidos para flutuar: o resultado será 3.0. Para declarar explicitamente os operandos como flutuante, usando o fsufixo ( 13f, 4f).

CodeCaster
fonte
+1 para explicar que você pode ter a resposta como flutuante, mas ainda assim faz divisão inteira. Além disso, outra maneira comum de forçar a divisão de ponto flutuante é multiplicar o primeiro termo da divisão por 1.0.
Xynariz
8

É apenas uma operação básica .

Lembre-se de quando você aprendeu a dividir. No começo nós resolvemos 9/6 = 1 with remainder 3.

9 / 6 == 1  //true
9 % 6 == 3 // true

O operador / em combinação com o operador% é usado para recuperar esses valores.

L. Monty
fonte
6

Pode ser útil:

double a = 5.0/2.0;   
Console.WriteLine (a);      // 2.5

double b = 5/2;   
Console.WriteLine (b);      // 2

int c = 5/2;   
Console.WriteLine (c);      // 2

double d = 5f/2f;   
Console.WriteLine (d);      // 2.5
eozten
fonte
Por favor, tente adicionar algumas explicações para sua resposta
NetStarter 8/16
A última expressão produzirá 2.5, não 2.
Lasse V. Karlsen
Sim, com erros ortográficos. Obrigado.
eozten
4

O resultado será sempre do tipo que possui o maior intervalo do numerador e do denominador. As exceções são byte e short, que produzem int (Int32).

var a = (byte)5 / (byte)2;  // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2;              // 2 (Int32)
var d = 5 / 2U;             // 2 (UInt32)
var e = 5L / 2U;            // 2 (Int64)
var f = 5L / 2UL;           // 2 (UInt64)
var g = 5F / 2UL;           // 2.5 (Single/float)
var h = 5F / 2D;            // 2.5 (Double)
var i = 5.0 / 2F;           // 2.5 (Double)
var j = 5M / 2;             // 2.5 (Decimal)
var k = 5M / 2F;            // Not allowed

Não há conversão implícita entre os tipos de ponto flutuante e o tipo decimal; portanto, a divisão entre eles não é permitida. É necessário converter e decidir explicitamente qual você deseja (o Decimal tem mais precisão e um intervalo menor comparado aos tipos de ponto flutuante).

zm
fonte