今天,我注意到当我将一个大于最大可能整数的double转换为整数时,我得到-2147483648。类似地,当我转换一个小于最小可能整数的double时,我也得到-2147483648。
是否为所有平台定义了此行为?
在/溢出下检测此问题的最佳方法是什么?在演示之前将if语句放入min和max int是最佳解决方案吗?
答案 0 :(得分:15)
将浮点数转换为整数时,溢出会导致未定义的行为。从C99规范, 6.3.1.4实数浮动和整数:
部分当实数浮动类型的有限值被转换为除
_Bool
以外的整数类型时,小数部分被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。
您必须手动检查范围,但不要使用:
之类的代码// DON'T use code like this!
if (my_double > INT_MAX || my_double < INT_MIN)
printf("Overflow!");
INT_MAX
是一个整数常量,可能没有精确的浮点表示。与float相比,它可以舍入到最接近的更高或最接近的可表示浮点值(这是实现定义的)。例如,对于64位整数,INT_MAX
为2^63 - 1
,通常会舍入为2^63
,因此检查基本上为my_double > INT_MAX + 1
。如果my_double
等于2^63
,则无法检测到溢出。
例如在Linux上使用gcc 4.9.1,以下程序
#include <math.h>
#include <stdint.h>
#include <stdio.h>
int main() {
double d = pow(2, 63);
int64_t i = INT64_MAX;
printf("%f > %lld is %s\n", d, i, d > i ? "true" : "false");
return 0;
}
打印
9223372036854775808.000000 > 9223372036854775807 is false
如果你事先不知道整数和双重类型的限制和内部表示,那么很难做到这一点。但是,例如,如果从double
转换为int64_t
,则可以使用精确双精度的浮点常量(假设两个补码和IEEE双精度数):
if (!(my_double >= -9223372036854775808.0 // -2^63
&& my_double < 9223372036854775808.0) // 2^63
) {
// Handle overflow.
}
构造!(A && B)
也正确处理NaN。 int
的便携,安全但略微不准确的版本是:
if (!(my_double > INT_MIN && my_double < INT_MAX)) {
// Handle overflow.
}
这样做会引起警告,并会错误地拒绝等于INT_MIN
或INT_MAX
的值。但对于大多数应用程序,这应该没问题。
答案 1 :(得分:12)
limits.h
具有整数数据类型的最大和最小可能值的常量,您可以在转换之前检查双变量,例如
if (my_double > nextafter(INT_MAX, 0) || my_double < nextafter(INT_MIN, 0))
printf("Overflow!");
else
my_int = (int)my_double;
编辑:nextafter()
将解决nwellnhof提到的问题
答案 2 :(得分:11)
回答你的问题:当你抛出范围浮动时的行为是未定义的或特定于实现的。
根据经验说明:我曾经在MIPS64系统上工作,根本没有实现这些类型的演员表。 CPU没有做出确定性的事情,而是抛出了CPU异常。应该模拟转换的异常处理程序,而不对结果做任何事情。
我最终得到了随机整数。猜猜追溯一个错误到这个原因需要多长时间。 :-)
如果您不确定该号码是否超出有效范围,您最好自行进行范围检查。
答案 3 :(得分:4)
C ++的可移植方式是使用SafeInt类:
http://www.codeplex.com/SafeInt
该实现将允许对C ++数字类型(包括强制转换)进行正常的加/减/等。每当检测到溢出情况时,它将抛出异常。
SafeInt<int> s1 = INT_MAX;
SafeInt<int> s2 = 42;
SafeInt<int> s3 = s1 + s2; // throws
我强烈建议在溢出是一个重要场景的任何地方使用这个类。它使得很难避免无声地溢出。如果存在溢出的恢复方案,只需捕获SafeIntException并根据需要进行恢复。
SafeInt现在适用于GCC和Visual Studio
答案 4 :(得分:3)
在/溢出下检测此问题的最佳方法是什么?
将double
附近的截断INT_MIN,INT_MAX
与完全限制进行比较。
技巧是完全将基于INT_MIN,INT_MAX
的限制转换为double
值。 double
可能不完全代表INT_MAX
,因为int
中的位数可能超过浮点数的精度。在这种情况下,INT_MAX
到double
的转换会受到舍入的影响。 INT_MAX
之后的数字是2的幂,当然可以表示为double
。 2.0*(INT_MAX/2 + 1)
生成的整数大于INT_MAX
。
同样适用于非2s补码机器上的INT_MIN
。
INT_MAX
始终是2的力量 - 1
始终INT_MIN
:
-INT_MAX
(不是2&#39;补充)或
-INT_MAX-1
(2&#39;补充)
int double_to_int(double x) {
x = trunc(x);
if (x >= 2.0*(INT_MAX/2 + 1)) Handle_Overflow();
#if -INT_MAX == INT_MIN
if (x <= 2.0*(INT_MIN/2 - 1)) Handle_Underflow();
#else
if (x < INT_MIN) Handle_Underflow();
#endif
return (int) x;
}
要检测NaN而不使用trunc()
#define DBL_INT_MAXP1 (2.0*(INT_MAX/2+1))
#define DBL_INT_MINM1 (2.0*(INT_MIN/2-1))
int double_to_int(double x) {
if (x < DBL_INT_MAXP1) {
#if -INT_MAX == INT_MIN
if (x > DBL_INT_MINM1) {
return (int) x;
}
#else
if (ceil(x) >= INT_MIN) {
return (int) x;
}
#endif
Handle_Underflow();
} else if (x > 0) {
Handle_Overflow();
} else {
Handle_NaN();
}
}
答案 5 :(得分:2)
另一种选择是使用boost::numeric_cast,它允许在数字类型之间进行任意转换。它会在转换数字类型时检测范围丢失,并在无法保留范围时抛出异常。
上面引用的网站还提供了一个小例子,可以快速概述如何使用这个模板。
当然,这不再是普通的C; - )
答案 6 :(得分:2)
我们遇到了同样的问题。如:
double d = 9223372036854775807L;
int i = (int)d;
在Linux /窗口中,i = -2147483648。但在AIX 5.3中i = 2147483647。
如果双倍超出整数范围。
答案 7 :(得分:0)
我不确定这一点,但我认为有可能为欠/溢出“打开”浮点异常...看看这个Dealing with Floating-point Exceptions in MSVC7\8所以你可能有一个替代if /别的检查。
答案 8 :(得分:-1)
我无法确定它是否为所有平台定义,但这几乎就是我使用的每个平台上发生的事情。除此之外,根据我的经验,它滚动。也就是说,如果double的值是INT_MAX + 2,那么当演员的结果最终为INT_MIN + 2时。
至于处理它的最好方法,我真的不确定。我自己也遇到了这个问题,并且尚未找到一种优雅的方式来处理它。我相信有人会回应,可以帮助我们。