开发者

How do I prevent/suppress SIGFPE in C++?

I'm trying to convert a double to float as well as various integer types inside a dll, which is used as a Game Maker extension. I don't need a sensible result if the double doesn't fit the range of the target types, so I simply used a static_cast.

Everything works as intended when I call this code from my own test C++ application, but when it's called from Game Maker, range errors raise SIGFPE for some reason, which leads Game Maker to terminate my program with an error message.

I don't need sensible results for out-of-range conversions, but crashing is a no-no. I tried using llround instead of a cast, but it also raises the signal.

I also tried catching the signal myself by using signal(SIGFPE, SIG_IGN); right before the conversion, but it didn't change the behaviour at all. Maybe the ominous comment in the mingw signal.h has something to do with that: "SIGFPE doesn't seem to work?"

I checked the source code of a different dll used in a Game Maker extension, and the binary provided by the author performs simple cast conversions without a problem. When I compile the source myself however, the SIGFPE problem is present again. I am guessing that the author used a different compiler, but I'd prefer to stay with mingw if possible.

So, how do I either perform these conversions safely, or prevent the signal from being generated when I perform them with a simple cast? I'm using mingw-g++ 4.5.0 to compile at the moment.

Here's the function where the problem happens:

template<typename ValueType>
static double writeIntValue(double handle, double value) {
    boost::shared_ptr<Writable> writable = handles.find<Writable>(handle);
    if(writable) {
        // Execution reaches this point
        ValueType converted = static_cast<ValueType>(value);
        // Execution doesn't reach this point if e.g. ValueType 
        // is short and value is 40开发者_运维百科000
        writable->write(reinterpret_cast<uint8_t *>(&converted), sizeof(converted));
    }
    return 0;
}


The good solution is to perform the conversion correctly by ensuring that the source value is within the range of the target type before casting. So my code from the question could be corrected like this:

ValueType converted;
if(value >= std::numeric_limits<ValueType>::max()) {
    converted = std::numeric_limits<ValueType>::max();
} else if(value <= std::numeric_limits<ValueType>::min()) {
    converted = std::numeric_limits<ValueType>::min();
} else {
    converted = static_cast<ValueType>(value);
}

Another option is to use numeric_cast from the Boost libraries, which throws an exception if the source value is out of range, so it has defined behaviour for all conversions.

The documentation of the Boost Numeric Conversion library contains some helpful information about how the standard defined certain conversions.

Thanks to rve for providing the correct suggestion in his answer, but unfortunately his example code is flawed, and I wanted to add some additional pointers that helped me.


Since you are using a DLL, are you sure the DLL is compiled in the same way as the program expects it? Maybe some 32/64 bit mismatch?

Also, SIGFPE can also be raised when there is an under/overflow when converting.

You can enable/disable the signal raised by this overflow by setting the mask using _FPU_SETCW (it's in fpu_control.h) My guess is that Game Maker enables this and your test program not.

I never tried this and I'm not sure mingw also has this but I hope this helps a little.

edit:

Why not making sure an overflow does not happen?

Something like:

if (value > std::numeric_limits<ValueType>::max())
{
   value = std::numeric_limits<ValueType>::max();
}
else if (value < std::numeric_limits<ValueType>::min())
{
   value = std::numeric_limits<ValueType>::min();
}
ValueType converted = value;


probably it's not related with conversion itself but with trying to access invalid memory (maybe stack corruptions or something like that). can you provide some code snippet?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜