在 Java 中计算双后小数点的位数的程序

Program to count number of digits in a double following decimal point in Java

提问人:millionleafclover 提问时间:9/27/2021 更新时间:9/28/2021 访问量:472

问:

下面是一个程序,它应该计算小数点后双精度值中的位数。在输入一些双精度值时,程序往往会启动无限循环(可能是由于浮点不精确)。我不想使用任何包装方法(包括 String 类)。有人可以解释某些输入的无限循环并提供解决方案吗?

import java.util.*;
class Flt
{
    public static void main(String args[])
    {
        Scanner sc = new Scanner(System.in);
        
        System.out.print("Enter a double number: ");
        double f = sc.nextDouble();
        double tmp = f;
        
        int len = 0;
        while(tmp != (int) tmp)
        {
            tmp *= 10;
            len++;
        }
        System.out.println(len);
    }
}
Java 浮点 精度

评论

0赞 Thomas 9/27/2021
造成这种情况的一个很可能的原因是浮点精度问题。最简单和最可靠的解决方案实际上是使用 or 字符串表示。除此之外,您可以尝试使用阈值来比较这些值(即,如果绝对差小于,假设 0.0000001,则认为它们相等 - 您需要找到一个合理的值)。请注意,即使是涉及的单个计算也已经可以更改分数位数(0.12345 * 10 已经是 1.23450000000000002)。BigDecimaldouble
0赞 Eric Postpischil 9/27/2021
Java 的格式使用二进制浮点格式,因此它没有任何小数位。试图计算小数位是对浮点运算工作原理的误解。double

答:

1赞 Eric Postpischil 9/27/2021 #1

问题在转换为 时溢出,因此从来都不是真的。inttmp != (int) tmp

考虑用户输入“3.1415”。3.1415 无法以格式表示,因此将其转换为最接近的可表示值 3.1415000000000000181188397618825547397136688232421875。首先,它有如此多的十进制数字,即使乘以 10 是用精确的实数算术执行的,它们也不会产生整数结果,直到数字达到 3141500000000000181188397618825547397136688232421875。但是,该数字不能转换为无溢出,因为它太大而无法用 .转换产生 2147483647 中可表示的最大值。然后,将3141500000000000181188397618825547397136688232421875数与转换结果(2147483647)进行比较,表明它们不相等,循环继续。doubleintintint

事实上,乘以 10 并不是用精确的实数算术来执行的。在每次乘法中,结果都四舍五入到中可表示的最接近的值。因此,第一个生成 31.415000000000002700062395888380706310272216796875,接下来的产生 314.150000000000003410605131648480892181396484375,依此类推。第一个整数结果是 31415000000000004。同样,它太大而无法用 表示,因此被计算为 ,这当然是真的,所以循环继续。doubleinttmp != (int) tmp31415000000000004 != 2147483647

无限循环可以通过消除到 的转换来解决。例如,测试表达式可以替换为 to 循环,只要除以 1 时有余数(因此不是整数)。但是,输入“3.1415”会产生 16 个 - 它不计算用户输入或扫描输入产生的小数位数,而是计算迭代次数,直到四舍五入的乘法产生整数。inttmp % 1 != 0tmpdouble

一旦用户的输入被转换为 ,就无法正确计算用户输入中的小数位数,因为原始值丢失了。如果用户输入“3.1415”或“3.141500000000000181188397618825547397136688232421875”,则结果将是 3.141500000000000181188397618825547397136688232421875,因此无法分辨原始号码是什么。要计算用户输入中的小数位数,请将其作为字符串读取,查找小数点,并计算其后面的数字字符(如果需要,不包括尾随零)。doubledouble

评论

0赞 millionleafclover 10/7/2021
那么,只是为了确认,没有万无一失的方法来通过数学语句计算实数中的位数吗?我必须使用 String 来应对不精确性?
0赞 Eric Postpischil 10/7/2021
@millionleafclover:一旦将数字转换为 ,就可以计算该值的十进制表示中的有效位数,但不可能知道原始数字中有多少位数字,然后再转换并四舍五入到最接近的。doubledoubledouble