简体   繁体   English

使用SEH时如何使用_controlfp_s?

[英]How do I use _controlfp_s when using SEH?

I have been trying to write some error-protection clauses for identifying problems in a dll which is provided to us by an third party. 我一直在尝试编写一些错误保护子句,以识别由第三方提供给我们的dll中的问题。 There may be problems in this dll (memory exceptions, floating point errors, etc), and it is advantageous to be able to identify these errors without access to the source code. 该dll中可能存在问题(内存异常,浮点错误等),并且能够在不访问源代码的情况下识别这些错误是有利的。

I have something put together from various SEH error handling routines, but although it works, there are several... inconsistencies with it. 我从各种SEH错误处理例程中收集了一些信息,但是尽管可以正常工作,但还是有一些...不一致之处。 I'm trying to isolate each one, and I'm going to ask a question on each one individually. 我正试图隔离每个人,并且我将分别对每个人提出一个问题。

This one is to do with using _controlfp_s to set and reset the floating point error behaviour. 这与使用_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.
    { }
}

This produces: 这将产生:

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

Notice that _previous_floating_point_control was actually set to the new value, so the effort to reset it at the end doesn't do anything. 请注意,_previous_floating_point_control实际上已设置为值,因此在最后将其重置的工作没有任何作用。

This is the correct behaviour (using _controlfp_s in two steps): 这是正确的行为(分两步使用_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

I presume that this is a bug, and since I have a way around it, I'm happy to ignore it. 我认为这是一个错误,并且由于我有解决方法,因此我很乐意忽略它。

It gets worse, though: if you notice, the above example (which works) sets the floating point word to be MINOR_FP_EXCEPTIONS, and this successfully picks up the SERIOUS_FP_EXCEPTIONS. 但是,情况变得更糟:如果您注意到了,上面的示例(有效)将浮点字设置为MINOR_FP_EXCEPTIONS,这成功获取了SERIOUS_FP_EXCEPTIONS。 If I try replacing this: 如果我尝试替换此:

_controlfp_s(&_previous_floating_point_control, SERIOUS_FP_EXCEPTIONS, _MCW_EM); 

Yields: 产量:

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); 

Yields: 产量:

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

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

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

So to summarise: 因此,总结一下:

  • _controlfp_s does not "get" the old floating point word as a by-product of the "set", but does if you do it in a separate operation. _controlfp_s不会“获取”旧的浮点字作为“集”的副产品,但是如果您在单独的操作中执行此操作,则可以。
  • To catch serious floating point errors, you need to set _controlfp_s to look for minor fpes and NOT major ones. 要捕获严重的浮点错误,您需要设置_controlfp_s来查找次要的fpes而不是主要的。
  • [The problem of failing to identify divide-by-zero is in one of the other questions] [无法识别被零除的问题是另外一个问题]

Am I just misunderstanding this whole area? 我只是误解了整个领域吗?

I think I will hazzard an answer to this one, since I've had a very useful response to my related question: How do I interpret GetExceptionCode results when using SEH? 我认为我会对此问题感到困惑,因为我对以下相关问题做出了非常有用的回答: 使用SEH时如何解释GetExceptionCode结果? .

Through experimentation, it seems as though the new settings to _controlfp_s are the exceptions to ignore, rather than the exceptions to react to. 通过实验,似乎_controlfp_s的新设置是要忽略的异常,而不是要响应的异常。 I found the official documentation on this... unclear. 我发现关于此的官方文档...不清楚。

On the issue of returning the old value to the fp word when setting a new one: I believe it is a bug - or at least undesired behaviour. 关于在设置新值时将旧值返回给fp字的问题:我认为这是一个错误-或至少是不良行为。 The documentation explicitly says that _controlfp_s will populate the old value when the mask is 0, but doesn't mention otherwise. 该文档明确指出_controlfp_s将在掩码为0时填充旧值,但未另外说明。 In almost all similar functions, I would expect you to be able to do the get and the set in one operation - rather than one function which operates either as a get or a set. 在几乎所有类似的函数中,我希望您能够在一个操作中完成get和set的操作,而不是一个以get set形式操作的函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 _controlfp_s中可能存在的错误可能无法正确恢复控制字 - Possible bug in _controlfp_s may not restore control word correctly 使用SEH时如何解释GetExceptionCode结果? - How do I interpret GetExceptionCode results when using SEH? 是否可以在运行时检查msvcrt.dll中的_controlfp_s? - Is it possible to check for _controlfp_s inside of msvcrt.dll at run-time? 使用 googlemock 时出现 SEH 异常 - SEH exception when using googlemock SEH在Linux中等效或如何处理OS信号(如SIGSERV)并继续保持 - SEH Equivalent in Linux or How do I handle OS Signals (like SIGSERV) and yet keep continuing 使用SEH(结构化异常处理)时观察到的不同行为 - Different behaviour observed when using SEH (structured exception handling) 运行exe时,我不需要使用QT的dll,而我想使用的是libs。我该怎么做? - I needn't use the QT's dlls and I want to use libs, when I run exe.How can I do this? 我何时/何时使用虚拟析构函数? - How/when do I use a virtual destructor? 我如何知道在使用ofstream时我达到了文件的最大大小? - How do I know I reached a file's maximum size when using ofstream? 我可以从Windows SEH异常中解码C ++异常吗? (如果是这样,如何?) - Can I decode a C++ exception from a Windows SEH exception? (And if so, how?)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM