如何测试存储在double中的值是否合适?(舍入是,但截断否)

are*_*naq 7 java double casting truncate long-integer

我认为这个问题很简单.但这是一个例子.

以下示例正常.我可以进行四舍五入,这里没有截断.

public static void main(String[] args) {
    double d = 9.9;
    long l = (long)d;
    System.out.println(l);
}
Run Code Online (Sandbox Code Playgroud)

输出:

9
Run Code Online (Sandbox Code Playgroud)

现在数字超出了长期范围:

public static void main(String[] args) {
    double d = 99999999999999999999999999999999.9;
    long l = (long)d;
    System.out.println(l);
}
Run Code Online (Sandbox Code Playgroud)

输出:

9223372036854775807
Run Code Online (Sandbox Code Playgroud)

这个让我烦恼.我不能继续使用完全不同的数字.我宁愿得到错误或异常.

有没有办法在Java中检测到这个?

Tag*_*eev 12

你可以将它与Long.MIN_VALUELong.MAX_VALUE:

public static boolean fitsLong(double d) {
    return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}
Run Code Online (Sandbox Code Playgroud)

更软化的方法是使用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
Run Code Online (Sandbox Code Playgroud)

这样您就可以控制舍入的执行方式.

你可能会问,为什么在以下方面存在严重的不平等fitsLong:d < Long.MAX_VALUE.实际上那是因为它Long.MAX_VALUE本身不能表示为双数.在转换时(double)Long.MAX_VALUE,double类型的精度不足以表示它,因此选择最接近的可表示值,即9223372036854775808.0(Long_MAX_VALUE+1.0).所以d <= Long.MAX_VALUE它会返回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
Run Code Online (Sandbox Code Playgroud)

那是因为你实际上并没有从中减去任何东西Long.MIN_VALUE.看到:

double d1 = Long.MIN_VALUE;
double d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true
Run Code Online (Sandbox Code Playgroud)

双精度不足以区分-9223372036854775808-9223372036854775809.9,因此它实际上是相同的双数.在编译期间,它被转换为二进制形式,这两个数字的二进制形式是相同的.因此,已经编译的程序不能区分是否-9223372036854775808-9223372036854775809.9在源代码中.

如果您认为它仍然是问题,请构建BigDecimal以下内容String:

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
Run Code Online (Sandbox Code Playgroud)