浮点迁移问题Floating-point migration issues

有时将项目升级到较新版本的 Visual Studio 后,可能会发现某些浮点操作的结果已更改。Sometimes when you upgrade your projects to a newer version of Visual Studio, you may find that the results of certain floating-point operations have changed. 出现这种情况通常由以下两个原因之一引起:为了更好地利用提供的处理器而进行的代码生成更改;C 运行时库 (CRT) 中数学函数所使用的算法的 bug 修复或更改。This generally happens for one of two reasons: Code generation changes that take better advantage of the available processor, and bug fixes or changes to the algorithms used in math functions in the C runtime library (CRT). 一般情况下,新的结果在语言标准指定的限制范围内是正确的。In general, the new results are correct to within the limits specified by the language standard. 请继续阅读,了解更改的内容,如果重要,请了解如何获得函数之前获得的同一结果。Read on to find out what's changed, and if it's important, how to get the same results your functions got before.

新的数学函数和通用的 CRT 更改New math functions and Universal CRT changes

多年来 Visual Studio 中提供了大多数的 CRT 数学函数,但从 Visual Studio 2013 开始,将包括 ISO C99 要求的所有函数。Most CRT math functions have been available in Visual Studio for years, but starting in Visual Studio 2013, all of the functions required by ISO C99 are included. 这些函数的实现是为了平衡性能与正确性。These functions are implemented to balance performance with correctness. 因为在每个用例中产生正确舍入的结果可能成本过高,这些函数旨在有效地生成接近正确舍入结果的近似结果。Because producing the correctly rounded result in every case may be prohibitively expensive, these functions are designed to efficiently produce a close approximation to the correctly rounded result. 在大多数情况下,虽然可能存在误差较大的情况,但生成的结果在正确舍入结果的 +/-1 最小精度单位或 ulp 范围内。In most cases, the result produced is within +/-1 unit of least precision, or ulp, of the correctly rounded result, though there may be cases where there is greater inaccuracy. 如果之前使用不同的数学库获取这些函数,实现差异可能会引起结果发生更改。If you were using a different math library to get these functions before, implementation differences may be responsible for the change in your results.

数学函数移到 Visual Studio 2015 中的通用 CRT 后,使用了一些新的算法并修复了 Visual Studio 2013 中新增函数的实现中的多个 bug。When the math functions were moved to the Universal CRT in Visual Studio 2015, some new algorithms were used, and several bugs in the implementation of the functions that were new in Visual Studio 2013 were fixed. 这些更改可能在使用这些函数的浮点计算结果中产生可检测差异。These changes can lead to detectable differences in the results of floating-point calculations that use these functions. 具有 bug 问题的函数有 erf、exp2、remainder、remquo、scalbln、scalbn 及其浮点型和长双精度型变体。The functions that had bug issues were erf, exp2, remainder, remquo, scalbln, and scalbn, and their float and long double variants. Visual Studio 2015 中的其他更改修复了保留 _clear87、_clearfp、fegetenv、fesetenv 和 feholdexcept 函数中浮点状态字和异常状态信息的问题。Other changes in Visual Studio 2015 fixed issues in preserving floating point status word and exception state information in _clear87, _clearfp, fegetenv, fesetenv, and feholdexcept functions.

处理器差异和编译器标志Processor differences and compiler flags

许多浮点数学库函数具有不同 CPU 体系结构的不同实现。Many of the floating point math library functions have different implementations for different CPU architectures. 例如,相比 64 位 x64 CRT,32 位 x86 CRT 可能具有不同的实现。For example, the 32-bit x86 CRT may have a different implementation than the 64-bit x64 CRT. 此外,某些函数可能有适用于给定 CPU 体系结构的多个实现。In addition, some of the functions may have multiple implementations for a given CPU architecture. 在运行时动态地选择最有效的实现,具体取决于受 CPU 支持的指令集。The most efficient implementation is selected dynamically at run-time depending on the instruction sets supported by the CPU. 例如,在 32 位 x86 CRT 中,一些函数同时具有 x87 实现和 SSE2 实现。For example, in the 32-bit x86 CRT, some functions have both an x87 implementation and an SSE2 implementation. 在支持 SSE2 的 CPU 上运行时,使用速度更快的 SSE2 实现。When running on a CPU that supports SSE2, the faster SSE2 implementation is used. 在不支持 SSE2 的 CPU 上运行时,使用速度较慢的 x87 实现。When running on a CPU that does not support SSE2, the slower x87 implementation is used. 迁移旧代码时可能会看到,因为 Visual Studio 2012 中默认的 x86 编译器体系结构选项已更改为 /arch:SSE2You may see this when migrating old code, because the default x86 compiler architecture option changed to /arch:SSE2 in Visual Studio 2012. 数学库函数的不同实现可能会使用不同的 CPU 指令和不同的算法来生成其结果,因此,这些函数可能会在各平台上产生不同的结果。Because different implementations of the math library functions may use different CPU instructions and different algorithms to produce their results, the functions may produce different results on different platforms. 在大多数情况下,结果在正确舍入结果的 +/-1 ulp 范围内,但实际结果在各 CPU 中可能会有所不同。In most cases, the results are within +/-1 ulp of the correctly rounded result, but the actual results may vary across CPUs.

将旧代码与新代码比较时,甚至在使用同一编译器标志时,Visual Studio 中不同的浮动点模式下提高代码生成的正确性也会影响浮点运算的结果。Code-generation correctness improvements in different floating point modes in Visual Studio can also affect the results of floating-point operations when old code is compared to new code, even when using the same compiler flags. 例如,指定 /fp:precise(默认值)或 /fp:strict 时,Visual Studio 2010 生成的代码可能未通过表达式正确传播中间非数字 (NaN) 值。For example, the code generated by Visual Studio 2010 when /fp:precise (the default) or /fp:strict was specified may not have propagated intermediate not-a-number (NaN) values through expressions correctly. 因此,在较旧的编译器中产生数值结果的某些表达式现在可能会产生正确的 NaN 结果。Thus, some expressions that gave a numeric result in older compilers may now correctly produce a NaN result. 你还可能看到差异,因为针对 /fp:fast 启用的代码优化现能够利用更多处理器功能。You may also see differences because the code optimizations enabled for /fp:fast now take advantage of more processor features. 这些优化可以使用更少的说明,但可能会影响生成的结果,因为某些先前可见的中间操作已删除。These optimizations can use fewer instructions, but may impact the generated results because some previously visible intermediate operations have been removed.

如何获得相同的结果How to get identical results

在大多数情况下,最新的编译器和库中的浮点更改的行为更快和/或更正确。In most cases, the floating-point changes in the newest compilers and libraries result in faster or more correct behavior, or both. 当 SSE2 指令替换 x87 说明时,你甚至可以看到更优秀的处理器性能。You may even see better processor power performance when SSE2 instructions replace x87 instructions. 但是,如果代码必须精确复制较旧编译器的浮点行为,请考虑使用 Visual Studio 本机多重目标功能,并使用较旧的工具集生成受影响的项目。However, if you have code that must precisely replicate the floating point behavior of an older compiler, consider using Visual Studio native multi-targeting capabilities, and build the affected project with the older toolset. 有关详细信息,请参阅 使用 Visual Studio 中的本机多重目标生成旧项目For more information, see Use native multi-targeting in Visual Studio to build old projects.

请参阅See also

从 Visual C++ 早期版本升级项目Upgrading Projects from Earlier Versions of Visual C++
潜在的升级问题概述 (Visual C++)Overview of potential upgrade issues (Visual C++)
Visual C++ 更改历史记录(2003 - 2015)Visual C++ change history 2003 - 2015