Por que o “f” é exigido ao declarar floats?

Exemplo:

float timeRemaining = 0.58f; 

Por que o f é exigido no final do número?

Sua declaração de float contém duas partes:

  1. Declara que a variável timeRemaining é do tipo float .
  2. Atribui o valor 0.58 a essa variável.

O problema ocorre na parte 2.

O lado direito é avaliado por conta própria. Acordo com a especificação do C #, um número contendo um ponto decimal que não tenha um sufixo é interpretado como um double .

Portanto, agora temos um valor double que queremos atribuir a uma variável do tipo float . Para fazer isso, deve haver uma conversão implícita de double para float . Não existe tal conversão, porque você pode (e neste caso) perder informações na conversão.

A razão é que o valor usado pelo compilador não é realmente 0.58, mas o valor de ponto flutuante mais próximo de 0.58, que é 0.57999999999999978655962351581366 … para double e exatamente 0.579999946057796478271484375 para float .

Estritamente falando, o f não é necessário. Você pode evitar ter que usar o sufixo f colocando o valor em um float :

 float timeRemaining = (float)0.58; 

Porque existem vários tipos numéricos que o compilador pode usar para representar o valor 0.58 : float , double e decimal . A menos que você esteja bem com o compilador escolhendo um para você, você tem que desambiguar.

A documentação para double informa que, se você não especificar o tipo por conta própria, o compilador sempre escolhe double como o tipo de qualquer literal numérico real:

Por padrão, um literal numérico real à direita do operador de atribuição é tratado como duplo. No entanto, se você quiser que um número inteiro seja tratado como double, use o sufixo d ou D.

Acrescentar o sufixo f cria um float ; o sufixo d cria um double ; o sufixo m cria um decimal . Todos estes também funcionam em maiúsculas.

No entanto, isso ainda não é suficiente para explicar por que isso não compila:

 float timeRemaining = 0.58; 

A metade ausente da resposta é que a conversão do double 0.58 para o float timeRemaining potencialmente perde informações, portanto, o compilador se recusa a aplicá-lo implicitamente. Se você adicionar uma conversão explícita, a conversão será executada; se você adicionar o sufixo f , nenhuma conversão será necessária. Em ambos os casos, o código seria compilado.

O problema é que .NET, para permitir que alguns tipos de operações implícitas sejam executadas envolvendo float e double , é necessário especificar explicitamente o que deve acontecer em todos os cenários envolvendo operandos mistos ou então permitir conversões implícitas entre os tipos a serem executados em uma direção só; A Microsoft optou por seguir o exemplo do Java ao permitir a direção que ocasionalmente favorece a precisão, mas freqüentemente sacrifica a correção e geralmente cria incômodo.

Em quase todos os casos, tomar o valor double que está mais próximo de uma quantidade numérica específica e atribuí-la a um float produzirá o valor float mais próximo da mesma quantidade. Existem alguns casos de canto, como o valor 9,007,199,791,611,905; a melhor representação de float seria 9.007.200.328.482.816 (que está desativada em 536.870.911), mas a melhor double representação (ou seja, 9.007.199.791.611.904) para float rende 9.007.199.254.740.992 (que está fora por 536.870.913). Em geral, porém, converter a melhor representação double de alguma quantidade em float produzirá a melhor representação de float possível ou uma de duas representações que sejam essencialmente igualmente boas.

Observe que esse comportamento desejável se aplica mesmo nos extremos; por exemplo, a melhor representação float para a quantidade 10 ^ 308 corresponde à representação float obtida convertendo a melhor representação double dessa quantidade. Da mesma forma, a melhor representação de float de 10 ^ 309 corresponde à representação de float obtida pela conversão da melhor representação double dessa quantidade.

Infelizmente, as conversões na direção que não exigem um casting explícito raramente são tão precisas. A conversão da melhor representação float de um valor para o double dificilmente produzirá algo particularmente próximo da melhor representação double desse valor e, em alguns casos, o resultado pode estar fora de centenas de ordens de magnitude (por exemplo, convertendo a melhor representação float de 10 ^ 40 para double produzirá um valor que compara maior que a melhor representação double de 10 ^ 300.

Infelizmente, as regras de conversão são o que são, portanto, é preciso conviver com tipografias e sufixos bobos ao converter valores na direção “segura” e ter cuidado com as tipologias implícitas na direção perigosa, que frequentemente resultará em resultados falsos.