如何测试存储在 double 中的值是否适合 long? (四舍五入是,但截断否)
How to test if value stored in double fit in long? (rounding yes, but truncating no)
我觉得这个问题很直白。但这里有一个例子。
下面的例子就可以了。我可以四舍五入,这里没有截断。
public static void main(String[] args) {
double d = 9.9;
long l = (long)d;
System.out.println(l);
}
输出:
9
现在数字超出了 long 的范围:
public static void main(String[] args) {
double d = 99999999999999999999999999999999.9;
long l = (long)d;
System.out.println(l);
}
输出:
9223372036854775807
这个让我很烦恼。我无法继续使用完全不同的号码。我宁愿得到错误或异常。
有什么方法可以在 Java 中检测到这个吗?
您可以将其与Long.MIN_VALUE
和Long.MAX_VALUE
进行比较:
public static boolean fitsLong(double d) {
return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}
更复杂的方法是使用 BigDecimal
:
double value = 1234567.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // 1234568
double value = 99999999999999999999999999999999.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
这样您就可以控制舍入的执行方式。
您可能会问,为什么 fitsLong
中存在严格的不等式:d < Long.MAX_VALUE
。其实那是因为 Long.MAX_VALUE
本身不能表示为双数。当您转换 (double)Long.MAX_VALUE
时,double
类型中没有足够的精度来表示它,因此选择了最接近的可表示值,即 9223372036854775808.0
(Long_MAX_VALUE+1.0
)。所以如果 d <= Long.MAX_VALUE
它会 return true
对于实际上更大一点的数字,因为在这个比较中 Long.MAX_VALUE
常量被提升为 double 类型。另一方面 Long.MIN_VALUE
可以精确地表示为 double
类型,因此这里我们有 >=
.
同样有趣的是为什么下面的方法有效:
double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9
System.out.println(fitsLong(value)); // returns true
那是因为你实际上没有从 Long.MIN_VALUE
中减去任何东西。参见:
double d1 = Long.MIN_VALUE;
double d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true
双精度不足以区分-9223372036854775808
和-9223372036854775809.9
,所以实际上是同一个double数。在编译过程中它被转换为二进制形式,这两个数字的二进制形式是相同的。因此,编译程序后,您无法区分源代码中是 -9223372036854775808
还是 -9223372036854775809.9
。
如果您觉得问题仍然存在,请从 String
:
构建 BigDecimal
long l = new BigDecimal("-9223372036854775808.2")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ok, -9223372036854775808
long l = new BigDecimal("-9223372036854775808.9")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
当您将浮点类型转换为 int
或 long
时,结果要么是最接近的整数(向零舍入),要么是 MIN_VALUE
或 MAX_VALUE
对于 int
或 long
。参见 JLS 5.1.3。
因此,一种替代方法是进行类型转换,然后测试适当的 MIN_VALUE
或 MAX_VALUE
。
请注意 Long.MAX_VALUE
是 9223372036854775807
...这是您的测试程序输出的数字!
(但是,如果您将浮点类型转换为 byte
、char
或 short
,这将不起作用。请参阅上面的 link解释。)
我觉得这个问题很直白。但这里有一个例子。
下面的例子就可以了。我可以四舍五入,这里没有截断。
public static void main(String[] args) {
double d = 9.9;
long l = (long)d;
System.out.println(l);
}
输出:
9
现在数字超出了 long 的范围:
public static void main(String[] args) {
double d = 99999999999999999999999999999999.9;
long l = (long)d;
System.out.println(l);
}
输出:
9223372036854775807
这个让我很烦恼。我无法继续使用完全不同的号码。我宁愿得到错误或异常。
有什么方法可以在 Java 中检测到这个吗?
您可以将其与Long.MIN_VALUE
和Long.MAX_VALUE
进行比较:
public static boolean fitsLong(double d) {
return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}
更复杂的方法是使用 BigDecimal
:
double value = 1234567.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // 1234568
double value = 99999999999999999999999999999999.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
这样您就可以控制舍入的执行方式。
您可能会问,为什么 fitsLong
中存在严格的不等式:d < Long.MAX_VALUE
。其实那是因为 Long.MAX_VALUE
本身不能表示为双数。当您转换 (double)Long.MAX_VALUE
时,double
类型中没有足够的精度来表示它,因此选择了最接近的可表示值,即 9223372036854775808.0
(Long_MAX_VALUE+1.0
)。所以如果 d <= Long.MAX_VALUE
它会 return true
对于实际上更大一点的数字,因为在这个比较中 Long.MAX_VALUE
常量被提升为 double 类型。另一方面 Long.MIN_VALUE
可以精确地表示为 double
类型,因此这里我们有 >=
.
同样有趣的是为什么下面的方法有效:
double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9
System.out.println(fitsLong(value)); // returns true
那是因为你实际上没有从 Long.MIN_VALUE
中减去任何东西。参见:
double d1 = Long.MIN_VALUE;
double d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true
双精度不足以区分-9223372036854775808
和-9223372036854775809.9
,所以实际上是同一个double数。在编译过程中它被转换为二进制形式,这两个数字的二进制形式是相同的。因此,编译程序后,您无法区分源代码中是 -9223372036854775808
还是 -9223372036854775809.9
。
如果您觉得问题仍然存在,请从 String
:
BigDecimal
long l = new BigDecimal("-9223372036854775808.2")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ok, -9223372036854775808
long l = new BigDecimal("-9223372036854775808.9")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
当您将浮点类型转换为 int
或 long
时,结果要么是最接近的整数(向零舍入),要么是 MIN_VALUE
或 MAX_VALUE
对于 int
或 long
。参见 JLS 5.1.3。
因此,一种替代方法是进行类型转换,然后测试适当的 MIN_VALUE
或 MAX_VALUE
。
请注意 Long.MAX_VALUE
是 9223372036854775807
...这是您的测试程序输出的数字!
(但是,如果您将浮点类型转换为 byte
、char
或 short
,这将不起作用。请参阅上面的 link解释。)