下面是一个程序,用来计算小数点后的双精度值的位数。在输入一些双精度值时,程序倾向于开始一个无限循环(可能是由于浮点不精确)。I不希望使用任何包装器方法(包括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);
}
}
问题在转换到int
时溢出,因此tmp != (int) tmp
永远不为真。
考虑用户输入"3.1415"。3.1415在double
格式中无法表示,因此将其转换为最接近的可表示值3.141500000000000181188397618825547397136688232421875。首先,它有如此多的十进制数字,以至于即使用精确的实数算术进行10的乘法运算,也不会产生整数结果,直到数字达到3141500000000000181188397618825547397136688232421875。但是,该数字不能在没有溢出的情况下转换为int
,因为它太大而无法在int
中表示。该转换产生int
中可表示的最大值2147483647。然后将3141500000000000181188397618825547397136688232421875与转换结果2147483647进行比较,表明它们不相等,并继续循环。
实际上,乘以10的乘法不是用精确的实数算术来执行的。在每次乘法中,结果四舍五入到double
中可表示的最接近的值。因此,第一个结果是31.415000000000002700062395888380706310272216796875,下一个是314.15000000000003410605131648480892181396484375,以此类推。第一个整数结果是31415000000000004。同样,这个值太大了,无法在int
中表示,因此tmp != (int) tmp
被求值为31415000000000004 != 2147483647
,这当然是正确的,因此循环继续进行。
可以通过消除到int
的转换来解决无限循环。例如,只要tmp
除以1有余数(因此不是整数),测试表达式就可以被tmp % 1 != 0
替换为循环。然而,如果输入"3.1415",则结果为16——它不计算用户输入中的小数位数,也不计算扫描结果的double
,而是计算迭代的次数,直到四舍五入相乘得到整数。
一旦用户的输入被转换为double
,就没有办法正确地计算用户输入中的小数位数,因为原始值丢失了。如果用户输入"3.1415"或"3.141500000000000181188397618825547397136688232421875",结果double
将是3.141500000000000181188397618825547397136688232421875,因此不可能知道原始数字是什么。要计算用户输入中的小数位数,将其作为字符串读取,查找小数点,并计算其后面的数字字符(如果需要,不包括后面的零)。