Compartilhar via


Problemas de migração de ponto flutuante

Às vezes, ao atualizar seus projetos para uma versão mais recente do Visual Studio, você poderá notar alterações nos resultados de determinadas operações de ponto flutuante. Isso geralmente ocorre por um desses dois motivos: alterações de geração de código que aproveitam melhor o processador disponível e correções de bugs ou alterações nos algoritmos usados em funções matemáticas no CRT (biblioteca de runtime C). Em geral, os novos resultados estão corretos e estão dentro dos limites especificados pelo padrão da linguagem. Continue lendo para descobrir o que mudou e se isso for importante, como obter os mesmos resultados obtidos antes pelas funções.

Novas funções matemáticas e alterações do CRT Universal

A maioria das funções matemáticas do CRT está disponível no Visual Studio há vários anos, mas a partir do Visual Studio 2013, todas as funções exigidas pela ISO C99 estão incluídas. Essas funções são implementadas para equilibrar o desempenho com exatidão. Uma vez que produzir o resultado arredondado corretamente pode ter um custo proibitivo em todos os casos, essas funções foram criadas para produzir de forma eficiente um resultado próximo ao resultado arredondado corretamente. Na maioria dos casos, o resultado produzido está dentro de +/-1 unidade de menor precisão, ou ulp, do resultado arredondado corretamente, embora possa haver casos em que há maior imprecisão. Se você usou uma biblioteca de matemática diferente para obter essas funções anteriormente, as diferenças de implementação podem explicar a alteração em seus resultados.

Quando as funções matemáticas foram movidas para o CRT Universal no Visual Studio 2015, alguns novos algoritmos foram utilizados e vários bugs na implementação das funções que eram novas no Visual Studio 2013 foram corrigidos. Essas alterações podem levar a diferenças detectáveis nos resultados de cálculos de ponto flutuante que usam essas funções. As funções que tinham problemas com bugs eram erf, exp2, remainder, remquo, scalbln, e scalbn, e suas variantes de float e de long double. Outras alterações no Visual Studio 2015 corrigiram problemas na preservação de palavra de status de ponto flutuante e informações de estado de exceção em _clear87, _clearfp, fegetenv, fesetenv e feholdexcept funções.

Diferenças de processador e sinalizadores de compilador

Muitas das funções da biblioteca de matemática de ponto flutuante têm implementações diferentes para diferentes arquiteturas de CPU. Por exemplo, o CRT x86 de 32 bits pode ter uma implementação diferente do CRT x64 de 64 bits. Além disso, algumas das funções podem ter várias implementações para uma determinada arquitetura de CPU. A implementação mais eficiente é selecionada dinamicamente em tempo de execução dependendo dos conjuntos de instruções com suporte da CPU. Por exemplo, no CRT x86 de 32 bits, algumas funções têm uma implementação x87 e uma implementação SSE2. Quando executado em uma CPU com suporte para SSE2, é usada a implementação SSE2 mais rápida. Ao executar em uma CPU que não dá suporte ao SSE2, a implementação x87 mais lenta é usada. Você poderá ver isso ao migrar o código antigo, porque a opção de arquitetura x86 padrão do compilador foi alterada para /arch:SSE2 no Visual Studio 2012. Uma vez que diferentes implementações das funções da biblioteca de matemática podem usar diferentes instruções de CPU e diferentes algoritmos para produzir seus resultados, as funções podem produzir resultados diferentes em plataformas diferentes. Na maioria dos casos, os resultados são dentro de +/-1 ulp do resultado arredondado corretamente, mas os resultados reais podem variar entre CPUs.

Melhorias na geração de código em vários modos de ponto flutuante também podem alterar os resultados de ponto flutuante ao comparar o código antigo com o novo código, mesmo com sinalizadores de compilador idênticos. Por exemplo, o código gerado pelo Visual Studio 2010 quando /fp:precise (o padrão) ou /fp:strict especificado pode não propagar valores intermediários não numéricos (NaN) por meio de expressões corretamente. Portanto, algumas expressões que forneciam um resultado numérico nos compiladores antigos agora podem produzir corretamente um resultado NaN. Você também poderá ver diferenças, porque as otimizações de código habilitadas para /fp:fast agora aproveitam mais recursos do processador. Essas otimizações podem usar menos instruções, mas podem afetar os resultados gerados, já que algumas operações intermediárias anteriormente visíveis foram removidas.

Como obter resultados idênticos

Na maioria dos casos, as alterações de ponto flutuante nas bibliotecas e nos compiladores mais novos resultam em um comportamento mais rápido ou mais correto, ou ambos. Você poderá até mesmo observar um melhor desempenho de energia do processador quando as instruções SSE2 substituem as instruções x87. No entanto, se você tiver um código que precise replicar precisamente o comportamento de ponto flutuante de um compilador mais antigo, considere usar recursos nativos de vários destinos do Visual Studio e crie o projeto afetado com as ferramentas de build mais antigas. Para obter mais informações, consulte Usar a multiplataforma nativa no Visual Studio para compilar projetos antigos.

Confira também

Como atualizar projetos de versões anteriores do Visual C++
Visão geral de possíveis problemas de atualização (Visual C++)
Histórico de alterações de 2003 a 2015 do Visual C++