如何在Java中将非常大的十进制数转换为二进制

6
例如,我将如何把 2^6012345678901234567890123456789012345678901234567890 转换成二进制呢?基本上,这些数字在Java中无法表示。
编辑:我将创建一个类来表示过大的数字。 我只是很难弄清如何将十进制转换为二进制。
编辑2:而且,我不允许使用BigDecimal、BigInteger或任何其他库,抱歉没有早些说明。
6个回答

6

这里有一段快速&肮脏(非常非常非常肮脏)的代码:

public class BigDec2Bin {

    public static int[] string2arrayReversed( String s )
    {
        char a[] = s.toCharArray();
        int  b[] = new int[ s.length() ];
        for( int i = 0; i < a.length; i++ )
        {
            b[a.length-1-i] = a[i] - 48;
        }
        return b;
    }

    // adds two binary numbers represented as strings
    public static String add( String s1, String s2 )
    {
        String result = "", stmp;
        int[] a1, a2;
        int ctmp, mark = 0;

        // a1 should be the longer one
        a1 = string2arrayReversed( ( s1.length() > s2.length() ? s1 : s2 ) );
        a2 = string2arrayReversed( ( s1.length() < s2.length() ? s1 : s2 ) );

        for( int i = 0; i < a1.length; i++ )
        {
            ctmp = a1[i] + ( i < a2.length ? a2[i] : 0 ) + mark;

            switch( ctmp )
            {
                default:
                case 0:
                    stmp = "0";
                    mark = 0;
                    break;
                case 1:
                    stmp = "1";
                    mark = 0;
                    break;
                case 2:
                    stmp = "0";
                    mark = 1;
                    break;
                case 3:
                    stmp = "1";
                    mark = 1;
                    break;
            }

            result = stmp + result;
        }

        if( mark > 0 ) { result = "1" + result; }

        return result;
    }

    public static String dec2bin( String s )
    {
        String result = "";

        for( int i = 0; i < s.length() ; i++ )
        {
            result = add( result + "0", result + "000" );
            result = add( result, Integer.toBinaryString( s.charAt(i) - 48 ) );
        }

        return result;
    }

    public static void main( String[] args )
    {
        String dec = "12345"; // should be 11000000111001
        System.out.println( "dec2bin( " + dec + " ) = " + dec2bin( dec ) );

        dec = "12345678901234567890123456789012345678901234567890";
        System.out.println( "dec2bin( " + dec + " ) = " + dec2bin( dec ) );
    }

}

输出:

十进制转二进制( 12345 ) = 011000000111001

十进制转二进制( 12345678901234567890123456789012345678901234567890 ) = 10000111001001111111011000110110100110101010111110000011110010100001010100000010011001110100011110101111100011000111111100011001011011001110001111110000101011010010


我的主要想法是始终使用字符串。

add 方法将两个表示为字符串的二进制数相加。
dec2bin 方法实现了这个魔法。

让我来解释一下:

result = add( result + "0", result + "000" );

这是一种将任意给定的数字乘以10的计算方法。

将二进制数乘以10与通过移位将该数加上一致:

x*10 <=> x<<1 + x<<3

result = add( result, Integer.toBinaryString( s.charAt(i) - 48 ) );

这段代码的作用是将下一个数字(从左到右)添加到结果字符串中。


基本上,我所做的是以1234为例:
0*10 + 1 = 1
1*10 + 2 = 12
12*10 + 3 = 123
123*10 + 4 = 1234

但只针对二进制(表示为字符串)。


希望能对您有所帮助,抱歉我的英语不太好。


4

试试这个:

new BigDecimal("12345678901234567890123456789012345678901234567890").toString(2);

编辑:

如果您想创建一个大数类,您可以参考我一周前发布的帖子。啊,这个问题是由您提出的,没关系。

在原理上,不同进制之间的转换是一个重复的“除法、余数、乘法、加法”操作。让我们看一个例子:

我们想把十进制数123转换为基数3的数字。我们该怎么做?

  1. 取模3的余数-将此数字作为结果的前导数字。
  2. 除以3。
  3. 如果数字大于0,则继续从步骤1开始进行。

所以它看起来像这样:

  • 123 % 3 == 0. ==> 最后一位数字是 0
  • 123 / 3 == 41
  • 41 % 3 == 2 ==> 倒数第二位数字是 2
  • 41 / 3 == 13
  • 13 % 3 == 1 ==> 第三位数字是 1
  • 13 / 3 == 4
  • 4 % 3 == 1 ==> 第四位数字再次是 1
  • 4 / 3 == 1
  • 1 % 3 == 1 ==> 第五位数字是 1

因此,我们得到的结果是 11120

问题在于,为此您需要已经有某种十进制格式下的3的除法,如果您没有在十进制格式(如我在上面链接到您上一个问题的答案中所做的那样)实现您的数字,则通常不会出现这种情况。

但是它适用于将内部数字格式转换为任何外部格式。


因此,让我们看一下如何进行反向计算,从11120(基数为3)到其十进制等价物。 (这里的基数3是任意基数的占位符,基数10是您内部基数的占位符。)原则上,可以将此数字写成以下形式:
1 * 3^4 + 1 * 3^3 + 1*3^2 + 2*3^1 + 0*3^0

一种更好的方法(计算速度更快)是这样的:

((((1 * 3) + 1 )*3 + 1 )*3 + 2)*3 + 0
    1
        3
             4
                12
                    13
                        39
                            41
                              123
                                  123

这被称为霍纳方案,通常用于计算多项式的值。

如果您知道如何在目标系统中表示输入基数(和数字),则可以在实现数字方案时实现此功能。

(我刚刚添加了这样一个计算到我的DecimalBigInt类中,但您可能希望直接在内部数据结构中进行计算,而不是为每个十进制数字创建一个或两个新的BigNumber类对象。)


@eLobato:是的,我发送后注意到了这一点。我添加了更多的提示。 - Paŭlo Ebermann

3
这种方法怎么样呢:
result = 0;
for each digit in the decimal number, from left to right
    result = result * 10 + digit;
return result;

我们需要一种表示任意大的二进制数并实现乘以10和小数相加的方法。

最直接的方法是用二进制位数组来表示一个任意大的二进制数。然后,您可以应用小学中学到的加法和乘法算法,只不过当数字超过1而不是9时,数字会“溢出”。例如:

  1010 * 1100111
----------------
        11001110 
+     1100111000
----------------
     10000000110

1
感谢您的帮助,对于一些数字来说这很有效。然而,数字6123456789012无法使用,但是这里有一个解决方法:
// a1 should be the longer one
a1 = string2arrayReversed( ( s1.length() >= s2.length() ? s1 : s2 ) ); //GREATER EQUAL 

0

如果您只使用整数,请使用BigInteger.toByteArray

如果不是,不幸的是BigDecimal没有这个方法。但我想,如果二进制形式仅用于传输而不是在任何地方进行计算,您总可以(在两种情况下)将数字的字符串表示ASCII编码。


我只会使用整数进行操作,这很有帮助。但是在这个任务中,我不被允许使用BigInteger或BigDecimal。 - frodosamoa
2
哦,如果这是一个作业,那么你就需要找出 BigInteger 使用的底层技术 :) - Bart van Heukelom
哈哈,好的。我完全不知道该怎么做。 - frodosamoa

0

有一个快速的程序可以得到一个巨大十进制数的二进制表示。 这个程序确实很快,它只需要20毫秒就可以处理一个有3000位数字的十进制数,例如:string(3000,'2')+'12345'。由于追求效率,它不太容易读懂。您可以自行修改它以使其更易于理解。

    inline string remove_pre_zero(const string& a)
{
    auto t = a.find_first_not_of('\0', 0);
    if (t == a.npos)
        return string("0");
    else
        return a.substr(t);
}

string convert_to_bin(const string& _s)
{
    const static string str[] = { "0", "1" };
    string s(_s.size(), '0');
    string binary;
    binary.reserve(_s.size()*3);
    int i = 0;
    for (const auto& c : _s)    
        s[i++] = (c - '0');

    while (s!="0")//simulate divide by 2
    {
        int t = 0, old_t = 0;
        for (auto& ch : s)
        {
            t = ((old_t * 10 + ch) & 1);
            ch = (ch + old_t * 10) >>1;
            old_t = t;
        }
        binary += str[t];
        if (s[0] == 0)
            s = remove_pre_zero(s);
    }   
        return string(binary.rbegin(), binary.rend());
}

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