A matemática de ponto flutuante é consistente em C #? Pode ser?

155

Não, essa não é outra pergunta "Por que (1 / 3,0) * 3! = 1" .

Ultimamente tenho lido sobre pontos flutuantes; especificamente, como o mesmo cálculo pode fornecer resultados diferentes em diferentes arquiteturas ou configurações de otimização.

Esse é um problema para jogos de vídeo que armazenam replays ou são rede ponto a ponto (em oposição ao servidor-cliente), que dependem de todos os clientes que geram exatamente os mesmos resultados sempre que executam o programa - uma pequena discrepância em um o cálculo de ponto flutuante pode levar a um estado de jogo drasticamente diferente em máquinas diferentes (ou mesmo na mesma máquina! )

Isso acontece mesmo entre os processadores que "seguem" o IEEE-754 , principalmente porque alguns processadores (ou seja, x86) usam precisão estendida dupla . Ou seja, eles usam registradores de 80 bits para fazer todos os cálculos e, em seguida, truncam para 64 ou 32 bits, levando a diferentes resultados de arredondamento do que máquinas que usam 64 ou 32 bits para os cálculos.

Eu já vi várias soluções para esse problema online, mas todas para C ++, não para C #:

  • Desative o modo de precisão estendida dupla (para que todos os doublecálculos usem IEEE-754 de 64 bits) usando _controlfp_s(Windows), _FPU_SETCW(Linux?) Ou fpsetprec(BSD).
  • Sempre execute o mesmo compilador com as mesmas configurações de otimização e exija que todos os usuários tenham a mesma arquitetura de CPU (sem reprodução entre plataformas). Como meu "compilador" é realmente o JIT, que pode otimizar diferentemente toda vez que o programa é executado , não acho que isso seja possível.
  • Use aritmética de ponto fixo e evite float-o doublecompletamente. decimalfuncionaria para esse fim, mas seria muito mais lento, e nenhuma das System.Mathfunções da biblioteca o suporta.

Então, isso é mesmo um problema em c #? E se eu pretender apenas suportar o Windows (não o Mono)?

Se for, existe alguma maneira de forçar meu programa a executar com precisão dupla normal?

Caso contrário, existem bibliotecas que ajudariam a manter os cálculos de ponto flutuante consistentes?

BlueRaja - Danny Pflughoeft
fonte
Eu já vi essa pergunta , mas cada resposta repete o problema sem solução ou diz "ignore", o que não é uma opção. Fiz uma pergunta semelhante sobre gamedev , mas (por causa do público) a maioria das respostas parece estar voltada para C ++.
BlueRaja - Danny Pflughoeft
1
não uma resposta, mas tenho certeza de que na maioria dos domínios que você poderia projetar seu sistema de tal forma que todo o estado compartilhado é determinística, e não há degradação de desempenho significativa por causa disso
driushkin
1
@ Peter você conhece alguma emulação rápida de ponto flutuante para .net?
CodesInChaos
1
Java sofre com esse problema?
Josué
3
@ Jos: Java tem a strictfppalavra - chave, o que força todos os cálculos a serem feitos no tamanho indicado ( floatou double), em vez de um tamanho estendido. No entanto, o Java ainda tem muitos problemas com o suporte ao IEE-754. Muito (muito, muito) poucas linguagens de programação suportam bem o IEE-754.
PORGES

Respostas:

52

Não conheço nenhuma maneira de tornar determinantes os pontos flutuantes normais em .net. É permitido ao JITter criar código que se comporte de maneira diferente em plataformas diferentes (ou entre versões diferentes do .net). Portanto, o uso de floats normais no código .net determinístico não é possível.

As soluções alternativas que considerei:

  1. Implemente FixedPoint32 em C #. Embora isso não seja muito difícil (eu tenho uma implementação parcialmente concluída), o intervalo muito pequeno de valores torna irritante o uso. Você deve ter cuidado o tempo todo para não transbordar nem perder muita precisão. No final, achei isso não mais fácil do que usar números inteiros diretamente.
  2. Implemente FixedPoint64 em C #. Achei isso bastante difícil de fazer. Para algumas operações, números inteiros intermediários de 128 bits seriam úteis. Mas .net não oferece esse tipo.
  3. Implemente um ponto flutuante personalizado de 32 bits. A falta de uma intrínseca BitScanReverse causa alguns aborrecimentos ao implementar isso. Mas atualmente acho que esse é o caminho mais promissor.
  4. Use código nativo para as operações matemáticas. Incorre na sobrecarga de uma chamada de delegado em todas as operações matemáticas.

Acabei de iniciar uma implementação de software de matemática de ponto flutuante de 32 bits. Ele pode fazer cerca de 70 milhões de adições / multiplicações por segundo no meu i3 de 2,66 GHz. https://github.com/CodesInChaos/SoftFloat . Obviamente, ainda é muito incompleto e com erros.

CodesInChaos
fonte
2
existe um número inteiro de tamanho "ilimitado" disponível no BigInteger, embora não seja tão rápido quanto o nativo int ou seja longo, existe. O .NET oferece esse tipo (criado para o F # acredito, mas pode ser usado em C #)
Rune FS
Outra opção é o wrapper GNU MP para .NET . É um invólucro da The GNU Multiple Precision Library que suporta números inteiros de precisão "inifinitos", racionais (frações) e números de ponto flutuante.
Cole Johnson
2
Se você fizer alguma dessas coisas, tente decimalprimeiro, pois é muito mais simples. Somente se for muito lento para a tarefa em questão vale a pena pensar em outras abordagens.
Roman Starkov
Eu aprendi sobre um caso especial em que os pontos flutuantes são determinísticos. A explicação que recebi é: Para multiplicação / divisão, se um dos números FP for uma potência de dois números (2 ^ x), significante / mantissa não será alterado durante o cálculo. Somente o expoente mudará (o ponto se moverá). Portanto, o arredondamento nunca acontecerá. O resultado será determinístico.
zigzag
Exemplo: Um número como 2 ^ 32 é representado como (expoente: 32, mantissa: 1). Se multiplicarmos isso por outro float (exp, man), o resultado será (exp + 32, man * 1). Para divisão, o resultado é (expo - 32, man * 1). Multiplicar a mantissa por 1 não altera a mantissa, portanto, não importa quantos bits ela tenha.
zigzag
28

A especificação C # (§4.1.6 Tipos de ponto flutuante) permite especificamente que os cálculos de ponto flutuante sejam feitos usando precisão maior que a do resultado. Então, não, acho que você não pode fazer esses cálculos determinísticos diretamente no .Net. Outros sugeriram várias soluções alternativas, para que você pudesse experimentá-las.

svick
fonte
9
Acabei de perceber que a especificação C # realmente não importa se distribuímos conjuntos compilados. Só importa se alguém deseja compatibilidade com a fonte. O que realmente importa é a especificação CLR. Mas tenho certeza de que as garantias são tão fracas quanto as garantias de C #.
CodesInChaos
A conversão para doublecada vez que uma operação não remove os bits indesejados, produzindo resultados consistentes?
IllidanS4 quer Monica de volta 27/02
2
@ IllidanS4 Eu não acho que isso garantiria resultados consistentes.
svick 27/02
14

A página a seguir pode ser útil no caso em que você precisa de portabilidade absoluta de tais operações. Ele discute o software para testar implementações do padrão IEEE 754, incluindo o software para emular operações de ponto flutuante. A maioria das informações provavelmente é específica para C ou C ++, no entanto.

http://www.math.utah.edu/~beebe/software/ieee/

Uma nota sobre ponto fixo

Os números binários de pontos fixos também podem funcionar bem como substitutos do ponto flutuante, como é evidente nas quatro operações aritméticas básicas:

  • Adição e subtração são triviais. Eles funcionam da mesma maneira que números inteiros. Basta adicionar ou subtrair!
  • Para multiplicar dois números de pontos fixos, multiplique os dois números e desloque para a direita o número definido de bits fracionários.
  • Para dividir dois números de pontos fixos, desloque o dividendo para a esquerda no número definido de bits fracionários e depois divida pelo divisor.
  • O capítulo quatro deste documento tem orientações adicionais sobre a implementação de números binários de pontos fixos.

Os números binários de pontos fixos podem ser implementados em qualquer tipo de dados inteiro, como int, long e BigInteger, e nos tipos não compatíveis com CLS uint e ulong.

Conforme sugerido em outra resposta, você pode usar as tabelas de pesquisa, em que cada elemento da tabela é um número de ponto fixo binário, para ajudar a implementar funções complexas, como seno, cosseno, raiz quadrada e assim por diante. Se a tabela de pesquisa for menos granular que o número do ponto fixo, é recomendável arredondar a entrada adicionando metade da granularidade da tabela de pesquisa à entrada:

// Assume each number has a 12 bit fractional part. (1/4096)
// Each entry in the lookup table corresponds to a fixed point number
//  with an 8-bit fractional part (1/256)
input+=(1<<3); // Add 2^3 for rounding purposes
input>>=4; // Shift right by 4 (to get 8-bit fractional part)
// --- clamp or restrict input here --
// Look up value.
return lookupTable[input];
Peter O.
fonte
5
Você deve fazer o upload para um site de projeto de código-fonte aberto, como sourceforge ou github. Isso facilita encontrar, contribuir com, colocar seu currículo etc. Além disso, algumas dicas de código-fonte (sinta-se à vontade para ignorar): use em constvez de staticconstantes para que o compilador possa otimizá-las; prefira funções membro a funções estáticas (para que possamos chamar, ex. em myDouble.LeadingZeros()vez de IntDouble.LeadingZeros(myDouble)); tentar evitar os nomes de variáveis de uma única letra ( MultiplyAnyLength, por exemplo, tem 9, o que torna muito difícil de seguir)
BlueRaja - Danny Pflughoeft
Tenha cuidado ao usar uncheckedtipos não compatíveis com CLS como ulong, uintetc. para fins de velocidade - porque são tão raramente usados, o JIT não os otimiza de maneira agressiva, portanto, usá-los pode ser realmente mais lento do que usar tipos normais como longe int. Além disso, o C # possui sobrecarga de operador , da qual esse projeto se beneficiaria bastante. Finalmente, existem testes de unidade associados? Além dessas pequenas coisas, trabalho incrível Peter, isso é ridiculamente impressionante!
BlueRaja - Danny Pflughoeft
Obrigado pelos comentários. Realizo testes de unidade no código. Eles são bastante extensos, porém, muito extensos para serem lançados por enquanto. Até escrevo rotinas auxiliares de teste de unidade para facilitar a escrita de vários testes. Por enquanto, não uso operadores sobrecarregados porque tenho planos de traduzir o código para Java quando terminar.
Peter O.
2
O engraçado é que, quando publiquei no seu blog, não percebi que o blog era seu. Eu tinha acabado de decidir experimentar o google + e, na sua faísca em C #, sugeriu a entrada no blog. Então pensei: "Que coincidência notável para nós dois começarmos a escrever uma coisa dessas ao mesmo tempo". Mas é claro que tivemos o mesmo gatilho :)
CodesInChaos
1
Por que se preocupar em portar isso para Java? Java já possui matemática determinística de ponto flutuante garantida via strictfp.
Antimony
9

Isso é um problema para c #?

Sim. Arquiteturas diferentes são a menor das suas preocupações, taxas de quadros diferentes etc. podem levar a desvios devido a imprecisões nas representações de flutuação - mesmo que sejam as mesmas imprecisões (por exemplo, mesma arquitetura, exceto uma GPU mais lenta em uma máquina).

Posso usar System.Decimal?

Não há razão para que você não possa, no entanto, é um cachorro lento.

Existe uma maneira de forçar meu programa a executar em dupla precisão?

Sim. Hospede o tempo de execução do CLR você mesmo ; e compile todas as chamadas / sinalizações necessárias (que alteram o comportamento da aritmética de ponto flutuante) no aplicativo C ++ antes de chamar CorBindToRuntimeEx.

Existem bibliotecas que ajudariam a manter os cálculos de ponto flutuante consistentes?

Não que eu saiba.

Existe outra maneira de resolver isso?

Já lidei com esse problema antes, a idéia é usar QNumbers . Eles são uma forma de reais com ponto fixo; mas não ponto fixo na base 10 (decimal) - em vez da base 2 (binária); por causa disso, as primitivas matemáticas nelas (add, sub, mul, div) são muito mais rápidas que os ingênuos pontos fixos da base 10; especialmente se nfor o mesmo para os dois valores (o que no seu caso seria). Além disso, por serem integrais, têm resultados bem definidos em todas as plataformas.

Lembre-se de que a taxa de quadros ainda pode afetá-los, mas não é tão ruim e é facilmente corrigida usando pontos de sincronização.

Posso usar mais funções matemáticas com QNumbers?

Sim, faça uma ida e volta decimal para fazer isso. Além disso, você realmente deve usar tabelas de pesquisa para as funções trig (sin, cos); como eles podem realmente dar resultados diferentes em plataformas diferentes - e se você os codificar corretamente, eles podem usar o QNumbers diretamente.

Jonathan Dickinson
fonte
3
Não tenho certeza do que você está falando com os framerates sendo um problema. Claramente, você gostaria de ter uma taxa de atualização fixa (veja, por exemplo, aqui ) - se é ou não o mesmo que a taxa de quadros da tela é irrelevante. Contanto que as imprecisões sejam as mesmas em todas as máquinas, estamos bem. Não entendo sua terceira resposta.
BlueRaja - Danny Pflughoeft
@BlueRaja: A resposta "Existe uma maneira de forçar meu programa a executar em dupla precisão?" equivaleria a reimplementar todo o Common Language Runtime, o que seria extremamente complicado, ou usando chamadas nativas para uma DLL C ++ do aplicativo C #, conforme sugerido na resposta do usuário shelleybutterfly. Pense em "Qnumbers" meramente como números de ponto binário fixo, como sugerido na minha resposta (Eu não tinha até agora visto números de ponto fixo binários sendo chamado de "Qnumbers".)
Peter O.
@ Pieter O. Você não precisa reimplementar o tempo de execução. O servidor em que trabalho na minha empresa hospeda o tempo de execução do CLR como um aplicativo C ++ nativo (o mesmo ocorre com o SQL Server). Eu sugiro que você pesquise no Google CorBindToRuntimeEx.
9788 Jonathan-
@BlueRaja depende do jogo em questão. A aplicação de etapas de taxa de quadros fixas a todos os jogos não é uma opção viável - porque o algoritmo AOE introduz latência artificial; o que é inaceitável em, por exemplo, um FPS.
Jonathan Dickinson
1
@ Jonathan: Esse é apenas um problema nos jogos ponto a ponto que enviam apenas a entrada - para esses, você precisa ter uma taxa de atualização fixa. A maioria dos FPSs não funciona assim, mas os poucos que possuem necessariamente uma taxa de atualização fixa. Veja esta pergunta .
BlueRaja - Danny Pflughoeft
6

De acordo com essa entrada do blog do MSDN, um pouco antiga, o JIT não usará SSE / SSE2 para ponto flutuante, é tudo x87. Por isso, como você mencionou, você precisa se preocupar com modos e sinalizadores, e no C # isso não é possível de controlar. Portanto, o uso de operações normais de ponto flutuante não garantirá exatamente o mesmo resultado em todas as máquinas do seu programa.

Para obter uma reprodutibilidade precisa da precisão dupla, você precisará fazer a emulação de ponto flutuante (ou ponto fixo) do software. Não conheço bibliotecas C # para fazer isso.

Dependendo das operações necessárias, você poderá escapar com precisão única. Aqui está a ideia:

  • armazene todos os valores de que você gosta com precisão única
  • para executar uma operação:
    • expandir entradas para dobrar a precisão
    • faça operação em dupla precisão
    • converter o resultado novamente em precisão única

O grande problema do x87 é que os cálculos podem ser feitos com precisão de 53 ou 64 bits, dependendo do sinalizador de precisão e se o registro foi derramado na memória. Porém, para muitas operações, executar a operação com alta precisão e arredondar para menor precisão garantirá a resposta correta, o que implica que a resposta será a mesma em todos os sistemas. Se você obtém precisão extra, não importa, pois você tem precisão suficiente para garantir a resposta certa em ambos os casos.

Operações que devem funcionar neste esquema: adição, subtração, multiplicação, divisão, sqrt. Coisas como pecado, exp, etc. não funcionam (os resultados geralmente correspondem, mas não há garantia). "Quando o arredondamento duplo é inócuo?" Referência do ACM (pagamento pago reg.)

Espero que isto ajude!

Nathan Whitehead
fonte
2
Também é um problema que o .NET 5, 6 ou 42, talvez não use mais o modo de cálculo x87. Não há nada no padrão que exija isso.
Eric J.
5

Como já foi dito por outras respostas: Sim, esse é um problema no C # - mesmo quando o Windows é puro.

Quanto a uma solução: você pode reduzir (e com algum esforço / desempenho) evitar completamente o problema se usar a BigIntegerclasse interna e escalar todos os cálculos para uma precisão definida usando um denominador comum para qualquer cálculo / armazenamento desses números.

Conforme solicitado pelo OP - em relação ao desempenho:

System.Decimalrepresenta número com 1 bit para um sinal e número inteiro de 96 bits e uma "escala" (representando onde está o ponto decimal). Para todos os cálculos que você faz, ele deve operar nessa estrutura de dados e não pode usar nenhuma instrução de ponto flutuante incorporada à CPU.

A BigInteger"solução" faz algo semelhante - apenas que você pode definir quantos dígitos precisa / deseja ... talvez você queira apenas 80 bits ou 240 bits de precisão.

A lentidão vem sempre da necessidade de simular todas as operações nesse número por meio de instruções somente inteiras sem usar as instruções internas da CPU / FPU, que por sua vez levam a muito mais instruções por operação matemática.

Para diminuir o impacto no desempenho, existem várias estratégias - como QNumbers (veja a resposta de Jonathan Dickinson - a matemática de ponto flutuante é consistente em C #? Pode ser? ) E / ou cache (por exemplo, cálculos trigonométricos ...) etc.

Yahia
fonte
1
Observe que BigIntegerestá disponível apenas no .Net 4.0.
svick
Meu palpite é que o desempenho atingido BigIntegerexcede até o desempenho do Decimal.
CodesInChaos 23/07
Algumas vezes, nas respostas aqui, há referência ao impacto no desempenho do uso Decimal(@ Jonathan Dickinson - 'dog slow') ou BigInteger(comentário do @CodeInChaos acima) - alguém pode fornecer uma pequena explicação sobre esses hits do desempenho e se / por que eles são realmente uma barreira para fornecer uma solução.
Barry Kaye
@Yahia - obrigado pela edição - leitura interessante, no entanto, você também pode dar um palpite sobre o desempenho de 'float' que não usa? Estamos falando 10% mais devagar ou 10 vezes mais devagar? deseja ter uma idéia da ordem de magnitude implícita.
Barry Kaye
-lo mais likeley na área de 1: 5 do que "apenas 10%"
Yahia
2

Bem, aqui seria a minha primeira tentativa de como fazer isso :

  1. Crie um projeto ATL.dll que tenha um objeto simples para ser usado nas operações críticas de ponto flutuante. certifique-se de compilá-lo com sinalizadores que desativam o uso de qualquer hardware que não seja xx87 para fazer ponto flutuante.
  2. Crie funções que chamam operações de ponto flutuante e retornem os resultados; comece simples e, se estiver funcionando para você, você sempre poderá aumentar a complexidade para atender às suas necessidades de desempenho posteriormente, se necessário.
  3. Coloque as chamadas control_fp em torno da matemática real para garantir que seja feito da mesma maneira em todas as máquinas.
  4. Consulte sua nova biblioteca e teste para garantir que ela funcione conforme o esperado.

(Eu acredito que você pode simplesmente compilar em uma DLL de 32 bits e usá-la com x86 ou AnyCpu [ou provavelmente apenas com o x86 em um sistema de 64 bits; veja o comentário abaixo]).

Então, supondo que funcione, se você quiser usar o Mono, imagino que você possa replicar a biblioteca em outras plataformas x86 de maneira semelhante (não COM, é claro; embora, talvez, com vinho? Um pouco fora da minha área uma vez nós vamos lá embora ...).

Supondo que você possa fazê-lo funcionar, você deve poder configurar funções personalizadas que podem executar várias operações ao mesmo tempo para corrigir quaisquer problemas de desempenho, e você terá uma matemática de ponto flutuante que permite obter resultados consistentes em plataformas com uma quantidade mínima do código escrito em C ++ e deixando o restante do código em C #.

shelleybutterfly
fonte
"compile para uma DLL de 32 bits e use ... AnyCpu" Acho que isso só funcionará quando executado em um sistema de 32 bits. Em um sistema de 64 bits, apenas uma segmentação de programa x86poderá carregar a dll de 32 bits.
CodesInChaos 23/07
2

Não sou desenvolvedor de jogos, embora tenha muita experiência com problemas computacionalmente difíceis ... então, farei o meu melhor.

A estratégia que eu adotaria é essencialmente esta:

  • Use um método mais lento (se necessário; se houver uma maneira mais rápida, ótimo!), Mas previsível para obter resultados reproduzíveis
  • Use double para todo o resto (por exemplo, renderização)

A curta duração disso é: você precisa encontrar um equilíbrio. Se você gasta 30ms de renderização (~ 33fps) e apenas 1ms faz a detecção de colisão (ou insere alguma outra operação altamente sensível) - mesmo se você triplicar o tempo necessário para fazer a aritmética crítica, o impacto que ela tem na taxa de quadros é você cai de 33,3fps para 30,3fps.

Sugiro que você analise tudo, explique quanto tempo é gasto em cada um dos cálculos visivelmente caros, repita as medições com 1 ou mais métodos para resolver esse problema e veja qual é o impacto.

Brian Vandenberg
fonte
1

A verificação dos links nas outras respostas deixa claro que você nunca terá garantia de que o ponto flutuante seja "corretamente" implementado ou se receberá sempre uma certa precisão para um determinado cálculo, mas talvez você possa fazer um melhor esforço (1) truncando todos os cálculos para um mínimo comum (por exemplo, se diferentes implementações fornecerem precisão de 32 a 80 bits, sempre truncando todas as operações para 30 ou 31 bits), (2) tenha uma tabela de alguns casos de teste na inicialização (casos limítrofes de adicionar, subtrair, multiplicar, dividir, sqrt, cosseno etc.) e se a implementação calcular valores correspondentes à tabela, não se preocupe em fazer os ajustes.

Mike
fonte
sempre truncando todas as operações para 30 ou 31 bits - é exatamente isso que o floattipo de dados faz nas máquinas x86 - no entanto, isso causa resultados ligeiramente diferentes das máquinas que fazem todos os seus cálculos usando apenas 32 bits e essas pequenas alterações se propagam ao longo do tempo. Portanto, a questão.
BlueRaja - Danny Pflughoeft
Se "N bits de precisão" significa que qualquer cálculo é preciso para tantos bits, e a máquina A tem precisão de 32 bits enquanto a máquina B tem precisão de 48 bits, os primeiros 32 bits de qualquer cálculo de ambas as máquinas devem ser idênticos. O truncamento para 32 bits ou menos após cada operação manteria ambas as máquinas exatamente sincronizadas? Caso contrário, o que é um exemplo?
Identificação da proteção de testemunha 44583292 03/03
-3

Sua pergunta é bastante difícil e técnica O_o. No entanto, posso ter uma ideia.

Você com certeza sabe que a CPU faz alguns ajustes após qualquer operação flutuante. E a CPU oferece várias instruções diferentes que fazem diferentes operações de arredondamento.

Portanto, para uma expressão, seu compilador escolherá um conjunto de instruções que o levará a um resultado. Mas qualquer outro fluxo de trabalho de instrução, mesmo que pretenda calcular a mesma expressão, pode fornecer outro resultado.

Os 'erros' cometidos por um ajuste de arredondamento aumentam a cada instrução adicional.

Como exemplo, podemos dizer que, no nível da montagem: a * b * c não é equivalente a a * c * b.

Não tenho muita certeza disso, você precisará pedir a alguém que conheça a arquitetura da CPU muito mais do que eu: p

No entanto, para responder à sua pergunta: em C ou C ++, você pode resolver o seu problema porque possui algum controle sobre o código da máquina gerado pelo seu compilador, mas no .NET você não tem nenhum. Portanto, desde que o código da sua máquina possa ser diferente, você nunca terá certeza do resultado exato.

Estou curioso para saber de que maneira isso pode ser um problema, pois a variação parece muito mínima, mas se você precisar de uma operação realmente precisa, a única solução em que posso pensar será aumentar o tamanho dos seus registros flutuantes. Use precisão dupla ou mesmo longa dupla, se puder (não tenho certeza se isso é possível usando a CLI).

Espero ter sido claro o suficiente, não sou perfeito em inglês (... de todo: s)

AxFab
fonte
9
Imagine um atirador de P2P. Você atira em um cara, bate nele e ele morre, mas é muito perto, você quase errou. No PC do outro cara, são usados ​​cálculos ligeiramente diferentes e calcula o que você sente falta. Você vê o problema agora? Nesse caso, aumentar o tamanho dos registros não ajudará (pelo menos não completamente). Usando exatamente o mesmo cálculo em cada computador.
svick 13/07
5
Nesse cenário, geralmente não se importa com a proximidade do resultado com o resultado real (desde que seja razoável), mas o que importa é que é exatamente o mesmo para todos os usuários.
CodesInChaos
1
Você está certo, eu não pensei sobre esse tipo de cenário. No entanto, estou de acordo com @CodeInChaos neste. Não achei muito inteligente tomar uma decisão importante duas vezes. Este é mais um problema de arquitetura de software. Um programa, o pedido do atirador como exemplo, deve fazer o cálculo e enviar o resultado para os outros. Você nunca terá erros dessa maneira. Você acertou ou não, mas apenas um tomou a decisão. Como dizer @driushkin
AxFab 13/07/11
2
@ Ases: Sim, é assim que a maioria dos atiradores trabalha; essa "autoridade" é chamada servidor, e chamamos a arquitetura geral de "cliente / servidor". No entanto, há outro tipo de arquitetura: ponto a ponto. No P2P, não há servidor; em vez disso, todos os clientes devem verificar todas as ações entre si antes que algo aconteça. Isso aumenta o atraso, tornando-o inaceitável para atiradores, mas diminui bastante o tráfego da rede, tornando-o perfeito para jogos em que um pequeno atraso (~ 250ms) é aceitável, mas a sincronização de todo o estado do jogo não é. Ou seja, jogos RTS como C&C e Starcraft usam P2P.
BlueRaja - Danny Pflughoeft
5
Em um jogo p2p, você não tem uma máquina confiável em que confiar. Se você permitir que uma estação decida se a bala acertou ou não, você abre a possibilidade de um cliente trapacear. Além disso, os links não conseguem lidar com a quantidade de dados que às vezes resulta - os jogos funcionam enviando os pedidos em vez dos resultados. Eu jogo RTS e muitas vezes eu já vi tantas porcarias voando por aí, de jeito nenhum elas poderiam ser enviadas por uplinks domésticos comuns.
Loren Pechtel