Existem muitos desafios técnicos que tornam extremamente difícil a reprodutibilidade exata dos resultados computacionais bit a bit.
No nível do software, alterações no código ou em qualquer uma das bibliotecas usadas pelo código podem obviamente causar resultados diferentes. Você ficaria surpreso com o número de bibliotecas de suporte que podem acabar vinculadas a um código científico típico.
Em um nível inferior, recompilar qualquer um dos códigos ou bibliotecas usadas pelo código com um novo compilador ou com diferentes otimizações do compilador ativadas também pode causar problemas. Um motivo é que várias operações no código podem ser executadas em uma ordem diferente quando o código é recompilado. Como a adição de ponto flutuante não é associativa (a + b) + c <> a + (b + c), isso pode gerar resultados diferentes.
OK, e se preservarmos todo o ambiente de software (SO, bibliotecas e código compilado) (por exemplo) gravá-lo em um CD-ROM inicializável que executará o código. Agora podemos ter certeza de que obteremos os mesmos resultados se executarmos esse código em um computador diferente?
Surpreendentemente, alguns códigos realmente variam a ordem dos cálculos com base nos aspectos do modelo de processador específico em que estão sendo executados. Por exemplo, as bibliotecas de álgebra linear otimizadas geralmente dividem as multiplicações de matriz para trabalhar em blocos que cabem no cache. Quando a Intel lança um novo microprocessador com um cache maior, o código pode ajustar dinamicamente o tamanho do bloco, resultando em aritmética executada em uma ordem diferente e fornecendo resultados diferentes. Outros códigos ajustam dinamicamente a ordem dos cálculos com base na quantidade de memória disponível - se você executar o código em um computador com mais memória que poderia fazer com que a aritmética fosse executada em uma ordem diferente e, assim, fornecer resultados diferentes.
As coisas ficam incrivelmente mais complicadas quando você insere um código multithread, pois o histórico exato de execução dos diferentes threads geralmente não é determinístico e isso pode novamente fazer com que operações aritméticas sejam executadas em uma ordem diferente de uma execução para a próxima.
Na prática, o máximo que você pode realmente esperar são resultados semelhantes de uma máquina para outra, até as tolerâncias de precisão dos algoritmos usados. por exemplo, se eu tiver um problema de localização de raiz e usar a bissecção para obter uma raiz dentro de + -1,0e-10, ficarei feliz desde que máquinas diferentes estejam produzindo respostas que concordem com essa tolerância.