在Java中将非常大的BigIntegers打印到文件中

3

我正在尝试将一个非常大的BigInteger打印到一个.txt文件中,但是当数字达到一定大小时,它不会打印任何东西。 代码:

BigInteger bi = new BigInteger("16777216");
int exponent = 1000000;
bi = bi.pow(exponent);

String txtToPrint = bi.toString();
sendToFile(txtToPrint, "output.txt");

private static void sendToFile(String txtToPrint, String fileName) {

        try {

            FileWriter fileWriter = new FileWriter(fileName);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            bufferedWriter.write(txtToPrint);

            bufferedWriter.close();
        }
        catch(IOException e) {
            System.out.println("Error writing to file '" + fileName + "'");
        }

}

每当指数大于566时,输出文件为空,而不是包含数字。目标是具有1000000甚至更大的指数。
我认为BigInteger没有大小限制,那么我的问题是:我超出了什么限制,是否有解决此问题的方法?
编辑:尝试刷新和关闭filewriter时,我得到了这个异常:
java.io.IOException: Stream closed
at sun.nio.cs.StreamEncoder.ensureOpen(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at PrintInt.main(PrintInt.java:4

编辑:该问题仅在Eclipse中运行程序时出现,我尝试将其导出为外部jar文件,一切都正常。我使用的是Eclipse Mars.1 Release(4.5.1)和Java jre1.8.0_131以及Cp1252编码。


顺便说一句:我认为BigInteger没有大小限制,但是数组的大小是有限制的,所以最多只能存储Integer.MAX_VALUE(减去某个值)位数字。但是使用那个指数,你应该没问题... - AxelH
我测试了你的代码,我的文件是正确的。7056KB... - AxelH
@AxelH,你有任何想法为什么它对你有效,而对我无效吗? - Rudy
我在这里指向BufferWriter... - AxelH
4个回答

2
我这里大部分都是猜测。我猜测问题来自于 BufferWriter
我们可以尝试更直接的方法,使用 Outputstream 并且使用资源关闭所有内容。
File f = new File(fileName);
try (
        FileOutputStream fos = new FileOutputStream(f);
        OutputStreamWriter os = new OutputStreamWriter(fos);
        Writer out = new BufferedWriter(os) 
){
    out.write(txtToPrint);
} catch(IOException e) {
    System.out.println("Error writing to file '" + fileName + "'");
}

我曾经遇到过一些操作系统同步问题,但文件从未丢失数据,数据只是稍有延迟地被复制...但为什么不在write之后等待操作系统验证数据是否已刷新到文件中(而不是在操作系统缓存中)呢?

out.flush(); //flush the stream
fos.getFD().sync(); //sync with the OS

那很有道理,但是数字停止在相同的指数(567)处打印到控制台和输出。 - Rudy
尝试了第二种方法,使用Outputstream,仍然没有打印。 - Rudy
@Rudy,你使用了相同的语法吗?因为你需要“关闭”所有内容以确保它被刷新。使用“try-with-resource”可以保证这一点。 - AxelH
是的,我使用了相同的语法。 - Rudy
我已经没有其他选择/想法了...最后我能说的是可能被卡在操作系统的缓存中...在多个操作系统环境下见过这种情况。使用fos.getFD().sync()等待操作系统将缓存数据转储到文件中。但在这里这并不重要...这是我最后的编辑,我只能猜测因为我无法复现这个问题...请编辑您的问题,包括Java版本、虚拟机配置和操作系统信息。 - AxelH
显示剩余4条评论

1
在Java 8中,BigInteger的javadoc添加了一些信息,提供了最小支持范围和当前实现的实际限制:
BigInteger must support values in the range -2Integer.MAX_VALUE (exclusive) to +2Integer.MAX_VALUE (exclusive) and may support values outside of that range.
实现注意事项: 当结果超出支持范围-2Integer.MAX_VALUE(不包括)至+2Integer.MAX_VALUE(不包括)时,BigInteger构造函数和操作会抛出ArithmeticException异常。

没错,但这里的问题不在于此,这里的值并不是很大。BigInteger 已经正确计算了。 - AxelH
1
谢谢您的回复。但是,抛出的异常会被打印出来吗?在我运行程序时并没有打印出任何错误信息。 - Rudy
@Rudy 我的想法是,也许实际字符串大小是问题所在。有人在下面提到过,但是如果你的堆大小的一半不足以容纳每个字符的2个字节,那么是否有可能是限制因素呢?如果是这样,就不会有异常和输出了。 - G2M
字符串 "only" 的长度为7224720个字符。你还有一些空间。堆空间可能会有,但这会抛出一个错误... - AxelH
@G2M 我认为堆大小可能是问题所在,因为该问题发生在指数低至600时。 - Rudy

0

我认为,字符串大小可能是限制因素。

你应该能够获得长度为Integer.MAX_VALUE的字符串(Java规范始终为2147483647(231-1),这是数组的最大大小,String类用于内部存储),或者是您最大堆大小的一半(因为每个字符占两个字节),以较小者为准。


0

当我们需要使用任意精度的非常大的数字时,就会用到它。需要注意的是,“任意”精度或数字位数并不意味着“无限制”:它意味着数字中的位数或计算中的精度位数受内存和/或我们指定的精度限制所限制。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接