如何在 C/C++ 中获得大数的精确二进制表示?

Posted

技术标签:

【中文标题】如何在 C/C++ 中获得大数的精确二进制表示?【英文标题】:How to get exact binary representation of big numbers in c/c++? 【发布时间】:2017-11-29 14:38:50 【问题描述】:

我正在尝试在 C/C++ 和 Java 中将大数转换为二进制,但如果我以纯十进制输入,如 998446744073709551615,输出是正确的,但如果我使用科学记数法前:1.7334e+32,那么二进制表示会出错.

我已经在 C/C++ 和 Java 中测试了二进制的 double 和 BigDecimals。

C/C++ 测试中的字符串到长双精度: https://ideone.com/EeOyNP

Java 测试中的字符串转大小数: https://ideone.com/OAvx7q

问题在于以超过64 bits 表示的数字在某种程度上没有以科学记数法表示。

查看下面的输出。

C/C++ 代码的输出:

输入 = 998446744073709551615 预期二进制 = 110110001000000011101101100111101111001100101011001111111111111111111

输出:成功解析 strtold(C 样式):9.98447e+20 二进制: 1101100010000000111011011001111011110011001010110100000000000000000000

stringstream 解析的字符串流(C++ 风格):9.98447e+20 二进制: 1101100010000000111011011001111011110011001010110100000000000000000000

showBitDiff 统计:总位数 70 位 49 位匹配 21 位 不匹配

Java 代码的输出:

十进制字符串部分:

十进制字符串:998446744073709551615 科学记数法:9.984467440737096E20

十进制字符串基数信息: 二进制: 110110001000000011101101100111101111001100101011001111111111111111111 十进制:998446744073709551615 十六进制:0x36203B67BCCACFFFFF 位长:70

指数字符串部分: 指数字符串:9.984467440737096E20

指数字符串基数信息: 二进制: 1101100010000000111011011001111011110011001010110100001011110100000000 十进制:998446744073709600000 六进制:0x36203B67BCCAD0BD00 位长:70

两个 BigInts 不相等

如何解决这个问题并在 C/C++ 中正确表示大数字? 我不想要 java 中的解决方案,我只是将 java 用于测试目的,因为它有 bigDecimal 类,用于非常大的任意数字,谢谢。

【问题讨论】:

哪种语言,C 或 C++?它们是不同的语言。 C++有std::bitsetstd::stringstd::vector,C语言没有。 @ThomasMatthews 是的,我知道 c++ 有 std::bitset 。但我的问题与两种语言 long double 有关,这两种语言都在 c/c++ 中,所以我想知道表示错误的地方。 @Ron 但问题不在于语言,它的数据类型在两种语言中都存在,即“长双精度数据类型”,所以这就是我在其中添加它的原因。 @Ron 好的,我将删除 c++ 标签,但我仍然认为没有必要这样做,因为我的问题已经用两种语言进行了测试。 【参考方案1】:

如果您首先转换为 double 以用指数表示来表示您的小数,那当然是错误的。看看https://en.wikipedia.org/wiki/Floating-point_arithmetic。 TLDR; 如果你有比州 (2^128) 更多的不同数字,你就会有差距。这就是 double 从一开始就设计的。如果您需要实际数字的精确表示,请不要将其转换为浮点表示。

【讨论】:

那么如果不将大数字转换为长双精度,我将如何获得大数字的精确二进制表示。因为我想要数字 > 64 位,并且只能用 long double 表示。 您是否能够/被允许使用库?此外,您得到的是有理数还是实数? 是的,我们可以使用内置库,我也已经在我的代码中使用过它们,我得到实数,然后填充零,这就是为什么我得到错误的二进制表示。但是对于 64 位,它们不代表确切的数字,而 long double 具有 96 位。 您确定您使用的是实数?您的示例和提示提示您仅使用整数/自然数。 实际上它首先使用它使用十进制整数并打印其二进制表示,然后它再次将十进制转换为科学记数法以获得长双精度,它可能是基于转换的实数。然后再次打印二进制文件,由于转换存在表示二进制文件的问题,我猜是四舍五入截断位。

以上是关于如何在 C/C++ 中获得大数的精确二进制表示?的主要内容,如果未能解决你的问题,请参考以下文章

double精确到几位小数

如何引发浮点异常

如何在 SAS 中精确地添加两个大数?

如何计算大数之间除法的第一个十进制数字?

如果分数不能以二进制精确表示,Double.toString() 如何工作?

模拟--大数加法