如何测试存储在 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_VALUELong.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.0Long_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

当您将浮点类型转换为 intlong 时,结果要么是最接近的整数(向零舍入),要么是 MIN_VALUEMAX_VALUE 对于 intlong。参见 JLS 5.1.3

因此,一种替代方法是进行类型转换,然后测试适当的 MIN_VALUEMAX_VALUE

请注意 Long.MAX_VALUE9223372036854775807 ...这是您的测试程序输出的数字!

(但是,如果您将浮点类型转换为 bytecharshort,这将不起作用。请参阅上面的 link解释。)