使用SEH时如何使用_controlfp_s?
How do I use _controlfp_s when using SEH?
我一直在尝试编写一些错误保护条款来识别第三方提供给我们的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 不会 "get" 旧的浮点字作为 "set" 的副产品,但如果您在单独的操作中执行它。
- 要捕获严重的浮点错误,您需要设置 _controlfp_s 以查找次要 f.p.e.s 而不是主要错误。
- [无法识别被零除的问题在其他问题之一]
我是不是误解了整个领域?
我想我会冒险回答这个问题,因为我对我的相关问题有一个非常有用的回答:。
通过实验,_controlfp_s 的新设置似乎是要忽略的异常,而不是要应对的异常。我找到了关于这个的官方文档...不清楚。
关于f.p返回旧值的问题。设置新的时说:我相信这是一个错误 - 或者至少是不受欢迎的行为。文档明确指出 _controlfp_s 将在掩码为 0 时填充旧值,但没有提及其他情况。在几乎所有类似的功能中,我希望您能够在一个操作中完成获取和设置 - 而不是一个作为获取 或 集合操作的函数。
我一直在尝试编写一些错误保护条款来识别第三方提供给我们的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 不会 "get" 旧的浮点字作为 "set" 的副产品,但如果您在单独的操作中执行它。
- 要捕获严重的浮点错误,您需要设置 _controlfp_s 以查找次要 f.p.e.s 而不是主要错误。
- [无法识别被零除的问题在其他问题之一]
我是不是误解了整个领域?
我想我会冒险回答这个问题,因为我对我的相关问题有一个非常有用的回答:
通过实验,_controlfp_s 的新设置似乎是要忽略的异常,而不是要应对的异常。我找到了关于这个的官方文档...不清楚。
关于f.p返回旧值的问题。设置新的时说:我相信这是一个错误 - 或者至少是不受欢迎的行为。文档明确指出 _controlfp_s 将在掩码为 0 时填充旧值,但没有提及其他情况。在几乎所有类似的功能中,我希望您能够在一个操作中完成获取和设置 - 而不是一个作为获取 或 集合操作的函数。