【问题标题】:How do I use _controlfp_s when using SEH?使用 SEH 时如何使用 _controlfp_s?
【发布时间】:2017-01-06 13:05:48
【问题描述】:

我一直在尝试编写一些错误保护条款来识别第三方提供给我们的 dll 中的问题。这个dll中可能存在问题(内存异常、浮点错误等),无需访问源代码就能够识别这些错误是有利的。

我从各种 SEH 错误处理例程中收集了一些东西,但是虽然它有效,但有几个......与它不一致。我正在尝试隔离每个人,我将分别针对每个人提出一个问题。

这与使用 _controlfp_s 设置和重置浮点错误行为有关。

// For floating point protection ---------
#include <float.h>      // defines of _EM_OVERFLOW, etc.
#include <string.h>     // strncpy_s & strncat_s
#include <stdlib.h>     // malloc
#include <excpt.h>      // EXCEPTION_EXECUTE_HANDLER
#include <iostream>     // cout
#include <bitset>       // bitset
#include <conio.h>      // _kbhit
#pragma fenv_access (on)
// ---------------------------------------


const unsigned int SERIOUS_FP_EXCEPTIONS = _EM_DENORMAL | _EM_ZERODIVIDE | _EM_INVALID;
const unsigned int MINOR_FP_EXCEPTIONS = _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT;

int main(int argc, char[])
{
    double numerator = 1.0;
    double denominator = 0.0;
    double result = 0.0;


    unsigned int _previous_floating_point_control;
    unsigned int _current_floating_point_control;

    _controlfp_s(&_current_floating_point_control, 0, 0);
    std::cout << "Floating point word originally:       " << std::bitset<32>(_current_floating_point_control) << std::endl;
    std::cout << "New settings to add:                  " << std::bitset<32>(MINOR_FP_EXCEPTIONS) << std::endl;
    std::cout << "With the mask:                        " << std::bitset<32>(_MCW_EM) << std::endl;

    _controlfp_s(&_previous_floating_point_control, MINOR_FP_EXCEPTIONS, _MCW_EM);      // This should appear to work, according to the documentation.
    std::cout << "Floating point word cached:           " << std::bitset<32>(_previous_floating_point_control) << std::endl;

    /* This works:
    _controlfp_s(&_previous_floating_point_control, 0, 0);      // This should appear to work, according to the documentation.
    _controlfp_s(nullptr, MINOR_FP_EXCEPTIONS, _MCW_EM);      // This should appear to work, according to the documentation.
    std::cout << "Floating point word cached:           " << std::bitset<32>(_previous_floating_point_control) << std::endl;
    */

    _controlfp_s(&_current_floating_point_control, 0, 0);
    std::cout << "Floating point word used:             " << std::bitset<32>(_current_floating_point_control) << std::endl;

    __try {
        result = numerator / denominator;
        _controlfp_s(NULL, _previous_floating_point_control, _MCW_EM);
        std::cout << "No error detected." << std::endl;
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        std::cout << "Error code is:                        " << std::bitset<32>(GetExceptionCode()) << std::endl;
        if ((GetExceptionCode() & _EM_INVALID) || (GetExceptionCode() & _EM_ZERODIVIDE) || (GetExceptionCode() & _EM_DENORMAL))
            std::cout << "ERROR! Serious floating point error!" << std::endl;
        else
            std::cout << "Something else..." << std::endl;

        _controlfp_s(NULL, _previous_floating_point_control, _MCW_EM);

        _controlfp_s(&_current_floating_point_control, 0, 0);
        std::cout << "Floating point word reset to:         " << std::bitset<32>(_current_floating_point_control) << std::endl;

    }

    std::cout << "result = " << result << std::endl;

    while (!_kbhit())   // Wait until a key is pressed to close console.
    { }
}

这会产生:

Floating point word originally:       00000000000010010000000000011111
New settings to add:                  00000000000000000000000000000111
With the mask:                        00000000000010000000000000011111
Floating point word cached:           00000000000010010000000000000111
Floating point word used:             00000000000010010000000000000111
ERROR! Serious floating point error!
Floating point word reset to:         00000000000010010000000000000111
result = 0

请注意,_previous_floating_point_control 实际上已设置为 new 值,因此在最后重置它的努力没有任何作用。

这是正确的行为(分两步使用_controlfp_s):

Floating point word originally:       00000000000010010000000000011111
New settings to add:                  00000000000000000000000000000111
With the mask:                        00000000000010000000000000011111
Floating point word cached:           00000000000010010000000000011111
Floating point word used:             00000000000010010000000000000111
ERROR! Serious floating point error!
Floating point word reset to:         00000000000010010000000000011111
result = 0

我认为这是一个错误,因为我有办法解决它,我很乐意忽略它。

但情况变得更糟:如果您注意到,上面的示例(有效)将浮点字设置为 MINOR_FP_EXCEPTIONS,这成功地获取了 SERIOUS_FP_EXCEPTIONS。如果我尝试替换它:

_controlfp_s(&_previous_floating_point_control, SERIOUS_FP_EXCEPTIONS, _MCW_EM); 

产量:

Floating point word originally:       00000000000010010000000000011111
New settings to add:                  00000000000010000000000000011000
With the mask:                        00000000000010000000000000011111
Floating point word cached:           00000000000010010000000000011111
Floating point word used:             00000000000010010000000000011000
No error detected.
result = 1.#INF


_controlfp_s(&_previous_floating_point_control, 0, _MCW_EM); 

产量:

Floating point word originally:       00000000000010010000000000011111
New settings to add:                  00000000000000000000000000000000
With the mask:                        00000000000010000000000000011111
Floating point word cached:           00000000000010010000000000011111
Floating point word used:             00000000000010010000000000000000
Error code is:                        11000000000000000000001010110101
ERROR! Serious floating point error!
Floating point word reset to:         00000000000010010000000000011111
result = 0

并且: _controlfp_s(nullptr, _MCW_EM, _MCW_EM); 产量:

Floating point word originally:       00000000000010010000000000011111
New settings to add:                  00000000000010000000000000011111
With the mask:                        00000000000010000000000000011111
Floating point word cached:           00000000000010010000000000011111
Floating point word used:             00000000000010010000000000011111
No error detected.
result = 1.#INF

总结一下:

  • _controlfp_s 不会将旧浮点字作为“集合”的副产品“获取”,但如果您在单独的操作中执行此操作,则会这样做。
  • 要捕获严重的浮点错误,您需要将 _controlfp_s 设置为查找较小的 f.p.e.s 而不是主要的。
  • [未能识别除以零的问题在其他问题之一中]

我只是误解了整个领域吗?

【问题讨论】:

    标签: c++ floating-point seh


    【解决方案1】:

    我想我会回答这个问题,因为我对我的相关问题有一个非常有用的回答:How do I interpret GetExceptionCode results when using SEH?

    通过实验,似乎 _controlfp_s 的新设置是要忽略的异常,而不是要做出反应的异常。我发现这方面的官方文档......不清楚。

    关于将旧值返回给f.p的问题。设置新的词时:我相信这是一个错误 - 或者至少是不受欢迎的行为。文档明确表示 _controlfp_s 将在掩码为 0 时填充旧值,但没有另外提及。在几乎所有类似的函数中,我希望您能够在一个操作中完成 get 和 set - 而不是一个作为 get set 操作的函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多