Por que os floats ainda fazem parte da linguagem Java quando os duplos são mais recomendados?

83

Em todos os lugares que eu vi, ele diz que double é superior a float em quase todos os aspectos. float foi tornado obsoleto por double em Java, então por que ainda é usado?

Eu programo muito com o Libgdx, e eles o forçam a usar float (deltaTime, etc.), mas parece-me que double é mais fácil de se trabalhar em termos de armazenamento e memória.

Eu também leio Quando você usa float e quando usa double , mas se float é realmente bom apenas para números com muitos dígitos após o ponto decimal, então por que não podemos simplesmente usar uma das muitas variações de double ?

Existe algum motivo pelo qual as pessoas insistem em usar carros alegóricos mesmo que não tenham mais vantagens? É muito trabalho para mudar tudo isso?

    
por Eames 26.04.2016 / 18:08
fonte

6 respostas

167

LibGDX é um framework usado principalmente para desenvolvimento de jogos.

No desenvolvimento de jogos, você geralmente tem que fazer um monte de cálculos numéricos em tempo real e qualquer performance que você possa conseguir. É por isso que os desenvolvedores de jogos geralmente usam float sempre que a precisão do float é boa o suficiente.

O tamanho dos registros FPU na CPU não é a única coisa que você precisa considerar neste caso. Na verdade, a maior parte do processamento pesado de dados no desenvolvimento de jogos é feita pela GPU e As GPUs geralmente são otimizadas para carros alegóricos, não duplas .

E depois há também:

    Largura de banda do barramento de memória
  • (com que rapidez você pode remover dados entre RAM, CPU e GPU)
  • cache da CPU (o que torna o anterior menos necessário)
  • RAM
  • VRAM

que são todos recursos preciosos dos quais você obtém o dobro quando usa 32bit float ao invés de 64bit double.

    
por 26.04.2016 / 19:41
fonte
57

Floats usam metade da memória de duplas.

Eles podem ter menos precisão do que o dobro, mas muitos aplicativos não exigem precisão. Eles têm um alcance maior do que qualquer formato de ponto fixo de tamanho semelhante. Portanto, eles preenchem um nicho que precisa de amplos intervalos de números, mas não precisa de alta precisão e onde o uso da memória é importante. Eu os usei para grandes sistemas de redes neurais no passado, por exemplo.

Movendo para fora do Java, eles também são amplamente usados em gráficos 3D, porque muitas GPUs os usam como seu formato primário - fora dos dispositivos NVIDIA Tesla / AMD FirePro muito caros, o ponto flutuante de precisão dupla é muito lento nas GPUs.

    
por 26.04.2016 / 19:51
fonte
47

Compatibilidade retroativa

Este é o motivo número um para manter o comportamento em uma linguagem / biblioteca / ISA já existente a> / etc.

Considere o que aconteceria se eles tirassem os floats de Java. Libgdx (e milhares de outras bibliotecas e programas) não funcionariam. Vai ser preciso um grande esforço para atualizar tudo, possivelmente anos para muitos projetos (basta olhar para a transição do Python 2 para Python 3). E nem tudo será atualizado, algumas coisas serão quebradas para sempre porque os mantenedores as abandonaram, talvez mais cedo do que teriam porque exigiriam mais esforço do que querem atualizar, ou porque não é mais possível para realizar o que seu software deveria fazer.

Desempenho

64 bits duplos ocupam o dobro da memória e quase sempre são mais lentos para processar do que 32 bit floats (raríssimas exceções onde se espera que a capacidade de float de 32 bits seja usada tão raramente ou não, que nenhum esforço foi feito para otimizar para eles.A menos que você esteja desenvolvendo para hardware especializado, você não vai experimentar isso em um futuro próximo.)

Especialmente relevante para você, o Libgdx é uma biblioteca de jogos. Os jogos tendem a ser mais sensíveis ao desempenho do que a maioria dos softwares. E as placas gráficas de jogos (por exemplo, AMD Radeon e NVIDIA Geforce, não FirePro ou Quadro) tendem a ter um desempenho de ponto flutuante de 64 bits muito fraco. Cortesia de Anandtech, aqui está como o desempenho de precisão dupla se compara ao desempenho de precisão única em algumas de Os da AMD e principais cartões de jogos da NVIDIA disponíveis (a partir do início de 2016)

AMD
Card    R9 Fury X      R9 Fury       R9 290X    R9 290
FP64    1/16           1/16          1/8        1/8

NVIDIA
Card    GTX Titan X    GTX 980 Ti    GTX 980    GTX 780 Ti
FP64    1/32           1/32          1/32       1/24

Observe que as séries R9 Fury e GTX 900 são mais recentes que as séries R9 200 e GTX 700, portanto, o desempenho relativo do ponto flutuante de 64 bits está diminuindo. Volte o suficiente e você encontrará a GTX 580, que tinha uma proporção de 1/8, como a série R9 200.

1/32 da performance é uma grande penalidade para pagar se você tiver uma restrição de tempo apertada e não ganhar muito usando o dobro maior.

    
por 26.04.2016 / 22:43
fonte
34

Operações atômicas

Além do que os outros já disseram, uma desvantagem específica de Java de double (e long ) é que atribuições a tipos primitivos de 64 bits não são garantidas como atomic . Da especificação da linguagem Java, Java SE 8 Edition , página 660 (ênfase adicionada):

17.7 Non-atomic Treatment of double and long

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Yuck.

Para evitar isso, você deve declarar a variável de 64 bits com o volatile palavra-chave, ou use alguma outra forma de sincronização das atribuições.

    
por 27.04.2016 / 07:53
fonte
3

Parece que outras respostas perderam um ponto importante: as arquiteturas SIMD podem processar menos / mais dados, dependendo se operam em double ou float structs (por exemplo, oito valores flutuantes por vez ou quatro valores duplos por vez).

Resumo das considerações de desempenho

  • float pode ser mais rápido em determinadas CPUs (por exemplo, determinados dispositivos móveis).
  • float usa menos memória, portanto, em grandes conjuntos de dados, pode reduzir substancialmente a memória total necessária (disco rígido / RAM) e a largura de banda consumida.
  • float pode fazer com que uma CPU consuma menos energia (não consigo encontrar uma referência, mas se não for possível pelo menos parece plausível) para cálculos de precisão única, em comparação com cálculos de precisão dupla.
  • float consome menos largura de banda e, em alguns aplicativos, é importante.
  • As arquiteturas SIMD podem processar até duas vezes a mesma quantidade de dados, porque normalmente.
  • float usa até metade da memória cache em comparação com o dobro.

Resumo das considerações de precisão

  • Em muitos aplicativos float é suficiente
  • double tem muito mais precisão de qualquer maneira

Considerações sobre compatibilidade

  • Se seus dados tiverem que ser enviados para uma GPU (por exemplo, para um videogame usando OpenGL ou qualquer outro API de renderização), o formato de ponto flutuante é consideravelmente mais rápido que double (isso porque os fabricantes de GPU tentam aumentar o número de núcleos gráficos e tentam economizar o máximo possível de circuitos em cada núcleo, otimizando para float permite criar GPUs com mais núcleos dentro)
  • GPUs antigas e alguns dispositivos móveis não podem aceitar double como o formato interno (para operações de renderização 3D)

Dicas gerais

  • Em processadores de desktop modernos (e provavelmente uma boa quantidade de processadores móveis), você pode basicamente supor que usar variáveis double temporárias na pilha fornece precisão extra gratuitamente (precisão extra sem penalidade de desempenho).
  • Nunca use mais precisão do que você precisa (você pode não saber quanta precisão você realmente precisa).
  • Às vezes, você é forçado apenas pelo intervalo de valores (alguns valores seriam infinitos se você estiver usando float , mas poderão ter valores limitados se você estiver usando double )
  • Usar apenas float ou apenas double ajuda muito o compilador a SIMD-ify as instruções.

Veja os comentários abaixo da PeterCordes para mais insights.

    
por 27.04.2016 / 19:16
fonte
0

Além das outras razões mencionadas:

Se você tiver dados de medição, sejam pressões, fluxos, correntes, voltagens ou o que for, isso geralmente é feito com o hardware que tem um ADC.

Um ADC normalmente tem 10 ou 12 bits, 14 ou 16 bits são mais raros. Mas vamos nos ater a 16 bit - se medindo em torno de escala completa, você tem uma precisão de 1/65535. Isso significa que uma mudança de 65534/65535 para 65535/65535 é apenas essa etapa - 1/65535. Isso é aproximadamente 1.5E-05. A precisão de um flutuador é em torno de 1E-07, portanto, muito melhor. Isso significa que você não perde nada usando float para armazenar esses dados.

Se você fizer cálculos excessivos com floats, terá um desempenho ligeiramente pior do que com doubles em termos de precisão, mas geralmente não precisa dessa precisão, já que muitas vezes você não se importa você acabou de medir uma voltagem de 2 V ou 2.00002 V. Da mesma forma, se você converter essa voltagem em uma pressão, você não se importa se você tem 3 bar ou 3.00003 bar.

    
por 29.04.2016 / 19:07
fonte