Por que você precisa flutuar / dobrar?

29

Eu estava assistindo http://www.joelonsoftware.com/items/2011/06/27.html e ri da piada de Jon Skeet sobre 0,3, não sendo 0,3. Pessoalmente, nunca tive problemas com flutuadores / decimais / duplos, mas lembro que aprendi 6502 muito cedo e nunca precisei de flutuadores na maioria dos meus programas. A única vez que o usei foi em gráficos e matemática, onde números imprecisos estavam corretos e a saída era para a tela e não para ser armazenada (em um banco de dados, arquivo) ou dependente.

Minha pergunta é: onde estão os lugares em que você normalmente usa carros alegóricos / decimais / duplo? Então eu sei cuidar dessas pegadinhas. Com dinheiro, uso comprimentos e armazeno valores em centavos, para velocidade de um objeto em um jogo, adiciono ints e divido (ou deslocamento de bits) o valor para saber se preciso mover um pixel ou não. (Eu fiz o movimento do objeto nos 6502 dias, não tínhamos divisão nem flutuação, mas tivemos turnos).

Então, eu estava principalmente curioso.

FrustratedWithFormsDesigner
fonte
10
porque importa muito que os juros que pago sobre minha hipoteca permaneçam 12,6 e que o número se torne 13, apenas porque 13 é um número redondo tão bom.
Chani
1
"Aprendi 6502 muito cedo e nunca precisei de flutuadores na maioria dos meus programas ... para velocidade de um objeto, adiciono ints e divido o valor para saber se movemos um pixel ou não". Essas são formas muito incomuns de realizar essas tarefas na prática moderna, exceto pela representação do dinheiro em centavos.
jprete
Coisa boa computador entender millicents.
tylermac
1
Ou, além disso, por que usar decimais quando podemos usar frações?
tylermac
6
@ Scrooge - ironicamente, você não pode representar 0,6 em um float.
Martin Beckett

Respostas:

28

Porque eles são, para a maioria dos propósitos, mais precisos que números inteiros.

Agora como é isso? "pela velocidade de um objeto em um jogo ..." este é um bom exemplo para esse caso. Digamos que você precise ter alguns objetos muito rápidos, como balas. Para poder descrever o movimento deles com variáveis ​​de velocidade inteira, é necessário garantir que as velocidades estejam no intervalo das variáveis ​​inteiras, o que significa que você não pode ter uma varredura arbitrariamente fina.

Mas então, você também pode descrever alguns objetos muito lentos, como o ponteiro das horas de um relógio. Como isso é cerca de 6 ordens de magnitude mais lento que os objetos de marcador, os primeiros ld (10⁶) ≈ 20 bits são zero, o que exclui os short inttipos desde o início. Ok, hoje temos longs em todos os lugares, o que nos deixa com 12 bits ainda confortáveis. Mas, mesmo assim, a velocidade do relógio será exata para apenas quatro casas decimais. Não é um relógio muito bom ... mas certamente está certo para um jogo. Apenas, você não gostaria de tornar a varredura muito mais grossa do que já é.

... o que leva a problemas se algum dia você desejar introduzir um novo tipo de objeto ainda mais rápido. Não há "espaço livre" restante.

O que acontece se escolhermos um tipo de flutuação? Mesmo tamanho de 32 bits, mas agora você tem 24 bits de precisão completos para todos os objetos. Isso significa que o relógio tem precisão suficiente para ficar sincronizado por segundos. As balas não têm precisão mais alta, mas apenas "vivem" por frações de segundo, então seria totalmente inútil se tivessem. E você não terá nenhum tipo de problema se quiser descrever objetos ainda mais rápidos (por que não a velocidade da luz? Sem problemas) ou objetos muito mais lentos. Você certamente não precisará dessas coisas em um jogo, mas às vezes precisa de simulações de física.

E com números de ponto flutuante, você obtém essa mesma precisão sempre e sem precisar primeiro escolher inteligentemente uma varredura não óbvia. Esse talvez seja o ponto mais importante, pois essas necessidades de escolha são muito suscetíveis a erros.

leftaroundabout
fonte
Inteiros são perfeitamente precisos. A imprecisão depende do cálculo incorreto.
Fjdumont 28/06
15
Os números inteiros são perfeitamente precisos somente quando você os usa para representar realmente números inteiros (ℤ). Representar qualquer outra coisa significa, de fato, cálculo incorreto. Nesse caso, você tem duas possibilidades: ou defina algum tipo que se adapte perfeitamente aos números que você realmente deseja representar. isso é possível, por exemplo, o Mathematica pode fazê-lo. Mas é muito complicado e caro, e geralmente não vale a pena, porque na verdade você não precisa de precisão perfeita. Mas você precisa de boa precisão, e é aí que os carros alegóricos geralmente fazem um trabalho melhor do que números inteiros.
leftaroundabout
53

Você os utiliza quando descreve um valor contínuo em vez de um valor discreto . Não é mais complicado descrever do que isso. Apenas não cometa o erro de assumir que qualquer valor com um ponto decimal é contínuo. Se mudar de uma só vez em pedaços, como adicionar um centavo, é discreto.

Karl Bielefeldt
fonte
28

Você realmente tem duas perguntas aqui.

Por que alguém precisa de matemática de ponto flutuante, afinal?

Como Karl Bielefeldt aponta, os números de ponto flutuante permitem modelar quantidades contínuas - e você as encontra em todo o lugar - não apenas no mundo físico, mas também em lugares como negócios e finanças.

Eu usei a matemática de ponto flutuante em muitas e muitas áreas da minha carreira em programação: química, trabalhando no AutoCAD e até escrevendo um simulador de Monte Carlo para fazer previsões financeiras. De fato, há um cara chamado David E. Shaw que usou técnicas de modelagem científica baseadas em ponto flutuante em Wall Street para gerar bilhões.

E, claro, há gráficos de computador. Eu consulto sobre o desenvolvimento de colírio para interfaces de usuário e tentar fazer isso hoje em dia sem uma sólida compreensão de ponto flutuante, trigonometria, cálculo e álgebra linear seria como aparecer em uma briga de armas com um canivete.

Por que alguém precisaria de um flutuador versus um duplo ?

Com IEEE 754 representações padrão, a 32-bit float dá-lhe cerca de 7 dígitos decimais de precisão, e expoentes no intervalo de 10 -38 a 10 38 . A 64-bit dupla dá-lhe cerca de 15 dígitos decimais de precisão, e expoentes no intervalo de 10 -307 a 10 307 .

Pode parecer que um carro alegórico seria suficiente para o que alguém razoavelmente precisaria, mas não é. Por exemplo, muitas quantidades do mundo real são medidas em mais de 7 dígitos decimais.

Mas, mais sutilmente, há um problema chamado coloquialmente "erro de arredondamento". As representações binárias de ponto flutuante são válidas apenas para valores cujas partes fracionárias têm um denominador com uma potência de 2, como 1/2, 1/4, 3/4, etc. Para representar outras frações, como 1/10, você "arredonda" o valor para a fração binária mais próxima, mas está um pouco errado - esse é o "erro de arredondamento". Então, quando você faz contas com esses números imprecisos, as imprecisões nos resultados podem ser muito piores do que as que você iniciou - às vezes as porcentagens de erro se multiplicam ou até se acumulam exponencialmente.

De qualquer forma, quanto mais dígitos binários você tiver que trabalhar, mais próxima estará sua representação binária arredondada do número que você está tentando representar, portanto, seu erro de arredondamento será menor. Então, quando você faz as contas, se tiver muitos dígitos para trabalhar, poderá executar muito mais operações antes que o erro de arredondamento acumulado se acumule até o local onde está um problema.

Na verdade, as dobras de 64 bits com seus 15 dígitos decimais não são boas o suficiente para muitos aplicativos. Eu estava usando números de ponto flutuante de 80 bits em 1985, e o IEEE agora define um tipo de ponto flutuante de 128 bits (16 bytes), para o qual posso imaginar usos.

Bob Murphy
fonte
2
+1 Bob, minha experiência com sistemas de controle de alta resolução como o telescópio para astronomia é que duplos de 64 bits não são bons o suficiente, a menos que você decida seus termos. O mesmo vale para controle de incêndio e de navegação de longo alcance
Tim Williscroft
20

É um equívoco comum, que em todo lugar que você lida com dinheiro, você deve armazenar seu valor como inteiro (centavos). Embora em alguns casos simples como a loja on-line seja verdade, se você tiver algo mais avançado, isso não ajuda muito.

Vamos dar um exemplo: um desenvolvedor ganha US $ 100.000 por ano. Qual é o salário exato do mês dele? Usando o número inteiro, você obtém o resultado $ 8333,33 (¢ 833333), que multiplicado por 12 é $ 99.999,96. Mantê-lo como número inteiro ajudou? Não, não.

Os bancos sempre usam valores decimais / inteiros? Bem, eles fazem por parte transacional. Mas, por exemplo, assim que você começa a falar sobre banco de investimento, com exceção de acompanhar as transações reais, tudo o resto é flutuante. Como é todo o código interno, você não o verá, mas pode obter um pico no QuantLib , que é essencialmente o mesmo (exceto muito mais limpo ;-).

Por que usar carros alegóricos? Porque usar decimal não ajuda em nada quando você usa funções como raiz quadrada, logaritmos, potências com expoentes não inteiros etc. E, é claro, os flutuadores são muito mais rápidos que os tipos decimais.

vartec
fonte
1
@ Job - decimais e carros alegóricos são muito diferentes. Você pode armazenar 0,1 exatamente em um tipo decimal, mas não em um número flutuante ou duplo.
Scott Whitlock
3
Eu tive outra pergunta. Se você pagou $100,000/12e usou um carro alegórico. Por que o resultado seria exatamente $ 100.000? Por que o float (ou decimal) não é arredondado para cima ou para baixo toda vez que alguém é pago? Estou a falar de quando se escreve um cheque (você não pode fazer 1/2 ou 1/3 um centavo) ou um depósito direto (i assumir que tem as mesmas limitações)
@acid: >>> x = 100000 / 12.0 >>> x * 12 100000.0
vartec
reler meu comentário? minha pergunta é quando eu uso o software para criar um cheque todo mês. Como não se pode pagar 1/2 centavo, como a pessoa recebe o valor total depois de um ano?
2
@ acid: você não pode usar a divisão reta, independentemente de usar inteiro, decimal ou dividir como float e depois arredondar. Esse é o ponto, usar decimal não ajuda nesse caso.
vartec
4

O que você descreveu são soluções perfeitas para situações em que você controla todas as entradas e saídas .

Na palavra real, não é o caso. Você precisará lidar com os sistemas que fornecem seus dados com algum valor real com algum grau de precisão e espera que você retorne os dados no mesmo formato. Nesses casos, você vai encontrar esses problemas.

De fato, você encontrará esses problemas, mesmo se usar os truques listados. Ao calcular o imposto de 17,5% sobre um preço, você receberá centavos fracionários, independentemente de armazenar o valor em dólares ou centavos. Você precisa acertar o arredondamento, pois o taxista fica muito chateado se não pagar o suficiente. Usar os moneytipos corretos (independentemente do idioma que você estiver usando) salvará você de um mundo de dores.

ChrisF
fonte
Qual é o tipo de dinheiro? (idioma ou link de referência) e por que esse tipo de 'correto'? É porque é ... 128bits ou mais alguma coisa? Meu outro por que usar meus 'truques' seria incorreto? Você tem um número inteiro em centavo. Se você multiplicá-lo por 0,175, obterá um número inteiro e o utilizará para o que quiser. Pensando no seu exemplo, acho que o float seria capaz de manter meu valor com precisão suficiente, mas não precisarei me preocupar com 0,3f == 0,3d sendo falso. -edit- e +1
1
@ acidzombie24 - Eu não quis dizer um tipo específico, mas qualquer que seja o tipo que seu idioma use para representar valores monetários. Além disso, se você tem 10 centavos e multiplica por 0,175, você tem 1,75 centavos - como você lida com isso com aritmética inteira? É 1 centavo ou 2 centavos? Entenda tudo errado e seu cliente pode acabar tendo muito dinheiro para o contribuinte.
ChrisF
Você nunca deve multiplicar 10 (um número inteiro) por 0,175 (um número real / flutuante) porque não deve misturar números exatos com números inexatos; o resultado será inexato. Em outras palavras, em um sistema de números exatos, um valor como 0,175 nunca existiria e, portanto, esse é um cálculo não sensorial. Uma solução melhor é multiplicar 10000 por 175 e inserir manualmente um ponto decimal, quando apropriado.
Barry Brown
8
@ Barry - eu sei. Eu estava tentando ilustrar o tipo de problema que você recebe. Também existe um valor como 0,175 se a taxa de imposto for de 17,5% e você precisar calcular o imposto sobre um item que custa 10 centavos.
ChrisF
1
@acidzombie: O tipo correto de usar para dinheiro é um decimal de ponto fixo com alta precisão (pelo menos 4 pontos decimais). Não quero nenhuma desculpa. Armazenar valores monetários em centavos não é suficiente, porque, na prática, fornece apenas dois pontos de precisão.
Aaronaught
3

"Deus criou os números inteiros, tudo o resto é obra do homem." - Leopold Kronecker (1886).

Por definição, você não precisa de outros tipos de números. A conclusão de Turing para uma linguagem de programação é baseada nos relacionamentos simples entre os vários tipos de números. Se você pode trabalhar com números inteiros (números naturais a / k / a), pode fazer qualquer coisa.

A pergunta é meio ilusória, porque você não precisa deles. Talvez você queira lugares onde seja conveniente ou ideal ou mais barato ou algo assim?

S.Lott
fonte
7
Também podemos dispensar números inteiros, pois também podemos construí-los usando apenas operações da teoria dos conjuntos e o conjunto vazio. Mas tanto isso quanto argumentar a partir da integralidade de Turing são o reducionismo acadêmico levado ao extremo.
Bob Murphy
4
Além disso, a integridade de Turing se aplica apenas à computação. Nem números inteiros nem mesmo racionais são matematicamente completos, pois nenhum deles está fechado para a convergência de seqüências de Cauchy. Então Kronecker estava cheio de ar quente: se você quiser um espaço completo que inclui os números inteiros, você tem que cair na real: xkcd.com/849
Bob Murphy
1
@ Bob Murphy: "reducionismo acadêmico levado ao extremo". Precisamente. A pergunta é ruim e leva a isso como resposta possível.
S.Lott
2

Em uma frase, os tipos decimais de ponto flutuante encapsulam a conversão para e de valores inteiros (que é tudo o que o computador sabe lidar no nível binário; não há ponto decimal no binário), fornecendo uma lógica, geralmente fácil de entender interface para cálculos de números decimais.

Francamente, dizer que você não precisa de números flutuantes porque você sabe como fazer matemática decimal usando números inteiros é como dizer que você sabe fazer aritmética à mão, então por que usar uma calculadora? Então você conhece o conceito; bravo. Não significa que você precise exercitar esse conhecimento o tempo todo. Muitas vezes, é mais rápido, mais barato e mais compreensível para um gênio não binário dizer simplesmente 3,5 + 4,6 = 8,1 em vez de converter os figos sig em uma quantidade inteira.

KeithS
fonte
1

A principal vantagem dos tipos de ponto flutuante é que, da perspectiva do tempo de execução, dois ou três formatos (desejo que mais idiomas suportem formatos de 80 bits) sejam suficientes para a maioria rápida dos propósitos computacionais. Se as linguagens de programação pudessem suportar facilmente uma família de tipos de pontos fixos, a complexidade do hardware necessária para um determinado nível de desempenho seria geralmente menor nos tipos de pontos fixos do que no ponto flutuante. Infelizmente, fornecer esse suporte está longe de ser "fácil".

Para que uma linguagem de programação satisfaça 98% das necessidades numéricas dos aplicativos com eficiência, ela deve incluir dezenas de tipos e fornecer operações de definição para o que podem ser centenas de combinações; além disso, mesmo se uma linguagem de programação tivesse um suporte maravilhoso a pontos fixos, alguns aplicativos ainda precisariam manter uma precisão relativa aproximadamente constante em uma faixa suficientemente grande para exigir o ponto flutuante. Dado que a matemática de ponto flutuante será necessária em algumas ocasiões, em qualquer caso, fazer com que os fornecedores de hardware se concentrem no desempenho da matemática com dois ou três formatos de ponto flutuante e fazer com que o código use esses formatos sempre que funcione razoavelmente bem, geralmente obtém melhores resultados. "economize dinheiro" do que tentaria otimizar o comportamento da matemática de ponto fixo.

Aliás, a matemática de ponto fixo era mais vantajosa nos processadores de 8 e 16 bits do que nos de 32 bits. Em um processador de 8 bits, em uma situação em que 32 bits não seriam suficientes, um tipo de 40 bits custaria apenas 25% mais espaço e 25-50% mais tempo que o tipo de 32 bits e exigiria 37,5% menos espaço e 37,5-60% menos tempo que um tipo de 64 bits. Em uma plataforma de 32 bits, se um tipo de 32 bits não for suficiente para algo, geralmente há poucas razões para usar algo menor que 64 bits. Se um tipo de ponto fixo de 48 bits for adequado, um "duplo" de 64 bits funcionará tão bem quanto o tipo de ponto fixo.

supercat
fonte
0

Geralmente, você deve ter muito cuidado ao usá-los. Entender a perda de precisão que pode surgir de cálculos simples é um desafio. Por exemplo, calcular uma lista de números como essa é uma péssima idéia:

double average(List<Double> data) {
  double ans = 0;
  for(Double d : data) {
    ans += d;
  }
  return ans / data.size();
}

O motivo é que, para listas suficientemente grandes, você basicamente perde todos os pontos de dados quando ansfica grande o suficiente (veja, por exemplo, isso ). O problema com esse código é que, para listas pequenas, provavelmente funcionará - é apenas em escala que ele quebra.

Pessoalmente, acho que você deve usá-los apenas quando: a) o cálculo realmente deve ser rápido; b) você não se importa com a probabilidade de o resultado estar muito distante (a menos que você realmente saiba o que está fazendo).

redjamjar
fonte
-1

Um pensamento é que você usaria as representações flutuante ou dupla quando precisar lidar com valores fora do intervalo inteiro.

As arquiteturas atuais (aproximadamente) têm um intervalo inteiro assinado de +/- 2.147.483.647 (32 bits) ou +/- 9.223.372.036.854.775.807 (64 bits). Não assinado estende isso por um fator de 2.

Os flutuadores IEEE 754 (aproximadamente) vão de +/- 1,4 × 10 ^ −45 a 3,4 × 10 ^ 38. Double estende esse intervalo para +/- 5 × 10−324 ± 2.225 × 10 ^ −308 com muitas condições e detalhes omitidos aqui.

Obviamente, a razão mais impressionante é que você precisa representar -0 ;-)

Stephen
fonte
Os números são principalmente de artigos da Wikipedia e devem ser ilustrativos. Exceto -0, isso é apenas por diversão.
Stephen
O problema é que existem muitos números inteiros nesse intervalo enorme que não são representados.
Barry Brown
@BarryBrown Absolutamente certo. "muitas condições e detalhes omitidos" embora.
Stephen
-1

O motivo usual é que eles são rápidos, pois a JVM geralmente usa suporte de hardware subjacente (a menos que você use strictfp).

Consulte https://stackoverflow.com/questions/517915/when-to-use-strictfp-keyword-in-java para saber o que strictfp implica.

Comunidade
fonte
Matemática de ponto flutuante é mais rápida que matemática de número inteiro? Em que processador os cálculos de ponto flutuante levam menos ciclos que os cálculos inteiros?
this.josh
1
@ this.josh, depende muito do número de dígitos que você tem nos seus números. Além disso, os números inteiros não podem ser divididos com precisão, o que pode ou não ser importante.
-2

É por isso que precisamos de sistemas operacionais de 256 bits.

O comprimento da prancha (a menor distância que você pode medir) = 10 ^ -35m
O universo observável é 14Bn parsecs em = 10 ^ 25m
Portanto, você pode medir qualquer coisa em unidades do comprimento da prancha como números inteiros, se você tiver apenas 200 bits de precisão.

Martin Beckett
fonte
2
-1: e se você estiver simulando coisas em uma escala maior que o universo observável?
amara
2
@sparkleshy, é para isso que servem os ponteiros FAR!
Martin Beckett