在Java中用浮点数乘整数时如何检测和防止整数溢出?
How to detect and prevent integer overflow when multiplying an integer by float in Java?
我已阅读这篇文章NUM00-J. Detect or prevent integer overflow and this question How does Java handle integer underflows and overflows and how would you check for it?。
如您所见,有很多解决方案可以防止整数与整数相乘时出现整数溢出。
但是我想知道有什么解决办法可以防止整数乘以浮点数时出现整数溢出吗?
我目前的(愚蠢的)解决方案:
public static final int mulInt(int a, float b) {
double c = a * b;
return c > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)c;
}
但是它有很多问题:
- 乘法执行可以得到预期的结果
两个参数都必须是小数的乘法。
- 当任何一个参数是大数字时,结果必然是
不正确(我知道部分原因是浮点数据类型)。
- 假设计算结果比
最大值为 double,势不可挡 return a
负数。
那么,这个问题的真正解决方案是什么?
您的回答将非常有帮助,我将不胜感激!
更新:这里还有另一个问题 How can I check if multiplying two numbers in Java will cause an overflow? 非常相似,但它是关于将一个整数乘以一个整数而不是乘以一个浮点数。
下面是一个 C 方法,可以阐明 Java。
在 赋值之前使用 double
而不是 float
数学 执行乘法以获得 double
的额外 precision/range .预计不会溢出。
像 c > Integer.MAX_VALUE
这样的比较会遇到 Integer.MAX_VALUE
首先被转换成 double
的问题。这可能会丢失精度。*1 考虑如果转换后的值为 Integer.MAX_VALUE + 1.0
会发生什么。那么如果 c
是 Integer.MAX_VALUE + 1.0
,代码将尝试 return (int) (Integer.MAX_VALUE + 1.0)
- 不好。最好使用格式正确的限制。 (也有负数。)在 C 中,可能 Java,浮点转换为 int
会截断小数。边缘附近需要特别小心。
#define INT_MAX_PLUS1_AS_DOUBLE ((INT_MAX/2 + 1)*2.0)
int mulInt(int a, float b) {
// double c = a * b;
double c = (double) a * b;
//return c > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)c;
if (c < INT_MAX_PLUS1_AS_DOUBLE && c - INT_MIN > -1.0) {
return (int) c;
}
if (c > 0) return INT_MAX;
if (c < 0) return INT_MIN;
return 0; // `b` was a NaN
}
c - INT_MIN > -1
类似于 c > INT_MIN - 1
,但由于 INT_MIN
是 2 的幂,因此 INT_MIN - 1
可能无法精确转换为 double
。 c - INT_MIN
预计准确接近边缘情况。
*1 当 int
是 32 位(或更少)并且 double
是 64 位(53 位有效)不是问题。但对于更广泛的整数类型很重要。
我已阅读这篇文章NUM00-J. Detect or prevent integer overflow and this question How does Java handle integer underflows and overflows and how would you check for it?。
如您所见,有很多解决方案可以防止整数与整数相乘时出现整数溢出。 但是我想知道有什么解决办法可以防止整数乘以浮点数时出现整数溢出吗?
我目前的(愚蠢的)解决方案:
public static final int mulInt(int a, float b) {
double c = a * b;
return c > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)c;
}
但是它有很多问题:
- 乘法执行可以得到预期的结果 两个参数都必须是小数的乘法。
- 当任何一个参数是大数字时,结果必然是 不正确(我知道部分原因是浮点数据类型)。
- 假设计算结果比 最大值为 double,势不可挡 return a 负数。
那么,这个问题的真正解决方案是什么?
您的回答将非常有帮助,我将不胜感激!
更新:这里还有另一个问题 How can I check if multiplying two numbers in Java will cause an overflow? 非常相似,但它是关于将一个整数乘以一个整数而不是乘以一个浮点数。
下面是一个 C 方法,可以阐明 Java。
在 赋值之前使用 double
而不是 float
数学 执行乘法以获得 double
的额外 precision/range .预计不会溢出。
像 c > Integer.MAX_VALUE
这样的比较会遇到 Integer.MAX_VALUE
首先被转换成 double
的问题。这可能会丢失精度。*1 考虑如果转换后的值为 Integer.MAX_VALUE + 1.0
会发生什么。那么如果 c
是 Integer.MAX_VALUE + 1.0
,代码将尝试 return (int) (Integer.MAX_VALUE + 1.0)
- 不好。最好使用格式正确的限制。 (也有负数。)在 C 中,可能 Java,浮点转换为 int
会截断小数。边缘附近需要特别小心。
#define INT_MAX_PLUS1_AS_DOUBLE ((INT_MAX/2 + 1)*2.0)
int mulInt(int a, float b) {
// double c = a * b;
double c = (double) a * b;
//return c > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)c;
if (c < INT_MAX_PLUS1_AS_DOUBLE && c - INT_MIN > -1.0) {
return (int) c;
}
if (c > 0) return INT_MAX;
if (c < 0) return INT_MIN;
return 0; // `b` was a NaN
}
c - INT_MIN > -1
类似于 c > INT_MIN - 1
,但由于 INT_MIN
是 2 的幂,因此 INT_MIN - 1
可能无法精确转换为 double
。 c - INT_MIN
预计准确接近边缘情况。
*1 当 int
是 32 位(或更少)并且 double
是 64 位(53 位有效)不是问题。但对于更广泛的整数类型很重要。