Por que o “f” é necessário ao declarar floats?

87

Exemplo:

float timeRemaining = 0.58f;

Por que é fobrigatório no final deste número?

Thomas
fonte
4
Provavelmente, porque de outra forma seria tratado como double.
Uwe Keim
2
0,58 (sem o sufixo f) é um literal do tipo double e não se pode atribuir um valor duplo a um float, assim como não se pode atribuir um valor int a uma string. No entanto, você pode atribuir um valor flutuante a um double porque aqui você está ampliando (C # irá converter isso implicitamente para você, pois nenhuma precisão será perdida).
Dave New
possível duplicata de Por que devemos usar literais em C #?
adatapost
@AVD Embora esta seja uma duplicata, o título da pergunta acima não produz seu link na lista de possíveis duplicatas. Portanto, isso pode agregar valor em termos de mais critérios de pesquisa, pelo menos.
Adam Houldsworth
1
@AdamHouldsworth - todo caminho vai para double & int .
adatapost

Respostas:

97

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

  1. Ele declara que a variável timeRemainingé do tipo float.
  2. Ele atribui o valor 0.58a esta variável.

O problema ocorre na parte 2.

O lado direito é avaliado por conta própria. De acordo com a especificação C #, um número contendo uma vírgula decimal que não possui um sufixo é interpretado como um double.

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

O motivo é 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 doublee exatamente 0,579999946057796478271484375 para float.

Estritamente falando, fnão é necessário. Você pode evitar ter que usar o fsufixo convertendo o valor para um float:

float timeRemaining = (float)0.58;
Jeffrey Sax
fonte
4
Duas perguntas, como você chegou ao número '0,579999946057796478271484375' e como vai (float) 0.58funcionar? Você disse anteriormente que não há conversão, porque informações podem ser perdidas, então como o elenco vai funcionar?
SexyBeast
8
1. Usei a calculadora do Windows, que usa até 34 dígitos. 2. Eu disse que não há conversão implícita . Uma conversão é uma conversão explícita, portanto, você está dizendo explicitamente ao compilador que deseja a conversão, e isso é permitido.
Jeffrey Sax
Interessante ... Espero que você ainda esteja monitorando esta postagem. Eu fui apresentado aos sufixos que encontraram 'm' anteriormente para o webcast que estou vendo. Como você fez a calculadora do Windows exibir 0,58 como o valor flutuante? Tentei alguns botões que pareciam que forçariam isso a acontecer, mas não ... continuei obtendo 0,58. Tenho que respeitar um homem que conhece suas ferramentas tão bem ...
user1585204 01 de
Eu entendo toda a coisa do F para float, mas por quê? Quando você declarou, a variável é declarada como flutuante, portanto, quando compilada, deve saber que esse valor é flutuante. E quando você declara o dobro da mesma coisa. Não faz sentido porque você tem float myvalue = 2.4; agora, quando compilado, o compilador já deve saber que float é o tipo de variável. Estou esquecendo de algo? Obrigado!
Frank G.
@FrankG. Duas razões: 1. A expressão 2.4é interpretada como um duplo em qualquer outro lugar. 2. Conversões de estreitamento implícitas (como de duplo para flutuante) não são permitidas. Se você deseja abrir uma exceção a essas regras, deve ter um bom motivo. Salvar 1 toque de tecla provavelmente não será suficiente.
Jeffrey Sax
36

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

A documentação para doubleafirma que, se você não especificar o tipo, o compilador sempre seleciona doublecomo o tipo de qualquer literal numérico real:

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

Anexar o sufixo fcria um float; o sufixo dcria um double; o sufixo mcria um decimal. Todos esses 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 que falta na resposta é que a conversão de em double 0.58para float timeRemainingpotencialmente perde informações, portanto, o compilador se recusa a aplicá-las implicitamente. Se você adicionar uma conversão explícita, a conversão será executada; se você adicionar o fsufixo, nenhuma conversão será necessária. Em ambos os casos, o código seria compilado.

Jon
fonte
mas como pode ser um duplo ou um decimal se a variável é um float, estou faltando algo
Thomas
@BlazArt Sim, a frase que afirma que o compilador nem mesmo tenta verificar o destino da atribuição. A razão para isso estará enterrada nas escolhas de design feitas pela equipe do compilador. Eu acho que é melhor ser explícito em face de uma possível confusão, ou seria muito caro se preocupar em implementar em vez de apenas ter duas regras, uma para inte outra para double.
Adam Houldsworth
@BlazArt: Acabei de definir a resposta, dê uma olhada novamente.
Jon
1
Em relação à perda de informações, você pode me dizer como é feito o elenco, em relação ao formato IEEE 754 em que são armazenados? Double tem maior precisão, então isso significa que lançar de float para double sempre funcionará, como double a = 0.69f;?
SexyBeast
@Cupidvogel: Sim, a especificação garante (em §6.1.2) que "as outras conversões numéricas implícitas nunca perdem nenhuma informação", o que inclui, entre outras, a conversão floatpara double.
Jon
2

O problema é que o .NET, para permitir que alguns tipos de operações implícitas sejam realizadas envolvendo floate double, precisava especificar explicitamente o que deveria acontecer em todos os cenários envolvendo operandos mistos ou então permitir conversões implícitas entre os tipos a serem realizadas em um direção apenas; A Microsoft escolheu seguir o exemplo de Java ao permitir a direção que ocasionalmente favorece a precisão, mas freqüentemente sacrifica a correção e geralmente cria problemas.

Em quase todos os casos, pegar o doublevalor que está mais próximo de uma determinada quantidade numérica e atribuí-lo a a floatproduzirá o floatvalor que está mais próximo dessa mesma quantidade. Existem alguns casos esquivos, como o valor 9.007.199.791.611.905; a melhor floatrepresentação seria 9.007.200.328.482.816 (que está fora por 536.870.911), mas lançando a melhor doublerepresentação (ou seja, 9.007.199.791.611.904) para floatrendimentos 9.007.199.254.740.992 (que está fora por 536.870.913). Em geral, porém, converter a melhor doublerepresentação de alguma quantidade em floatproduzirá a melhor floatrepresentação possível ou uma das duas representações que são essencialmente igualmente boas.

Observe que esse comportamento desejável se aplica mesmo aos extremos; por exemplo, a melhor floatrepresentação para a quantidade 10 ^ 308 corresponde à floatrepresentação obtida ao converter a melhor doublerepresentação dessa quantidade. Da mesma forma, a melhor floatrepresentação de 10 ^ 309 corresponde à floatrepresentação alcançada ao converter a melhor doublerepresentação dessa quantidade.

Infelizmente, as conversões na direção que não exige um elenco explícito raramente são tão precisas. Converter a melhor floatrepresentação de um valor em doubleraramente produzirá algo particularmente próximo à melhor doublerepresentação desse valor e, em alguns casos, o resultado pode estar errado por centenas de ordens de magnitude (por exemplo, converter a melhor floatrepresentação de 10 ^ 40 em doubleresultará um valor comparável maior do que a melhor doublerepresentação de 10 ^ 300.

Infelizmente, as regras de conversão são o que são, então é preciso conviver com o uso de typecasts e sufixos bobos ao converter valores na direção "segura", e ter cuidado com as typecasts implícitas na direção perigosa que freqüentemente produzirá resultados falsos.

supergato
fonte