如何将一个非常大的整数(以字符串格式)转换为十六进制格式?(C#)

17

给定一个可能非常大的整数值(以C#字符串格式),我想能够生成其十六进制等效项。通常的方法在这里不适用,因为我们谈论的是任意大的数字,50位或更多。我看到的技术使用像这样的技术:

// Store integer 182
int decValue = 182;
// Convert integer 182 as a hex in a string variable
string hexValue = decValue.ToString("X");
// Convert the hex string back to the number
int decAgain = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);

由于要转换的整数过大,所以无法使用。

例如,我需要能够将这样的字符串转换为其十六进制等价物:

843370923007003347112437570992242323

以下方法都不能用:

C# 将整数转换为十六进制并再次转回整数 如何在 C# 中转换十六进制和十进制数字?


'%x' % 843370923007003347112437570992242323。糟糕!对不起,这是Python。;-) - Marcelo Cantos
只是出于好奇,你需要将这些数字存储在字符串中吗?那似乎浪费了很多位。你可以将两个数字存储在字节(BCD)中的一对中。 - Seth Moore
1
我收到的数字是十进制的。我的任务是将它们转换为十六进制。 - eviljack
2
你正在计算宇宙中的原子数量吗? - Mesh
2
或者他正在计算比尔·盖茨的税单。 - Jeremy
4个回答

25

哦,这很简单:

        var s = "843370923007003347112437570992242323";
        var result = new List<byte>();
        result.Add( 0 );
        foreach ( char c in s )
        {
            int val = (int)( c - '0' );
            for ( int i = 0 ; i < result.Count ; i++ )
            {
                int digit = result[i] * 10 + val;
                result[i] = (byte)( digit & 0x0F );
                val = digit >> 4;
            }
            if ( val != 0 )
                result.Add( (byte)val );
        }

        var hex = "";
        foreach ( byte b in result )
            hex = "0123456789ABCDEF"[ b ] + hex;

是的,这确实是一个非常好的解决方案,如果追求性能,这比创建BigInteger对象更好。但是为了可维护性,这会增加代码大小和复杂性。因此,我只会在非常紧密的循环或某种核心消息处理程序中使用它。但仍然+1提供基于循环的转换。 - Davy Landman
这是一个垫脚石。为了提高性能,你显然不会使用List<byte>,而是足够大的byte[]。当你做到这一点时,它将会非常快速 - Dan Byström
是的,有多种方法,您可以预估数组的最大宽度,直接使用字符数组,并仅使用所需的数字。然后,您可以仅从该结果数组的正确子集创建字符串。 - Davy Landman
1
你是否对其进行了分析,以确定它是否比使用BigInteger的直接实现更快?对我来说,结果看起来就像是一个穷人版的大整数实现。 - starblue
这是一个非常好的解决方案! - Hesham Eraqi

7

使用BigInteger来存储整数,然后在该对象上使用.ToString("X")

示例:

var number = BigInteger.Parse("843370923007003347112437570992242323");
string hexValue = number.ToString("X");

然而,这仅限于.NET 4及更高版本。但Jens A.指出了CodeProject上的BigInteger类,该类包含一个名为ToHexString的方法,因此可以在.NET 4以下的场景中使用。

很不错的尝试,但我只能使用 .NET 3.5 及以下版本。 - eviljack
6
.NET 3.5及以下版本有一个很好的BigInteger实现,可以在这里找到: http://www.codeproject.com/KB/cs/biginteger.aspx - Jens

2
如Jens所说,看看Code Project上的BigInt实现。即使他们没有转换为十六进制的函数,只要这个BigInt有除法和模运算(我不认为它有模函数,所以您还需要自己编写模运算),你也可以轻松地编写一个函数来完成转换。请注意保留HTML标签。

1

在stackoverflow上,已经有很多关于十进制和十六进制转换的好方法了......但是我需要处理巨大的整数和分数,且几乎不损失精度,所以我改进了所有我找到的代码,并且分享一些(没有使用大整数或实数库)。

//---------------------------------------------------------------------------
AnsiString str_hex2dec(const AnsiString &hex)
    {
    char c;
    AnsiString dec="",s;
    int i,j,l,ll,cy,val;
    int  i0,i1,i2,i3,sig;
    sig=+1; l=hex.Length();
    if (l) { c=hex[l]; if (c=='h') l--; if (c=='H') l--; }
    i0=0; i1=l; i2=0; i3=l;
    for (i=1;i<=l;i++)      // scan for parts of number
        {
        char c=hex[i];
        if (c=='-') sig=-sig;
        if ((c=='.')||(c==',')) i1=i-1;
        if ((c>='0')&&(c<='9')) { if (!i0) i0=i; if ((!i2)&&(i>i1)) i2=i; }
        if ((c>='A')&&(c<='F')) { if (!i0) i0=i; if ((!i2)&&(i>i1)) i2=i; }
        if ((c>='a')&&(c<='f')) { if (!i0) i0=i; if ((!i2)&&(i>i1)) i2=i; }
        }

    l=0; s=""; if (i0) for (i=i0;i<=i1;i++)
        {
        c=hex[i];
             if ((c>='0')&&(c<='9')) c-='0';
        else if ((c>='A')&&(c<='F')) c-='A'-10;
        else if ((c>='a')&&(c<='f')) c-='A'-10;
        for (cy=c,j=1;j<=l;j++)
            {
            val=(s[j]<<4)+cy;
            s[j]=val%10;
            cy  =val/10;
            }
        while (cy>0)
            {
            l++;
            s+=char(cy%10);
            cy/=10;
            }
        }
    if (s!="")
        {
        for (j=1;j<=l;j++) { c=s[j]; if (c<10) c+='0'; else c+='A'-10; s[j]=c; }
        for (i=l,j=1;j<i;j++,i--) { c=s[i]; s[i]=s[j]; s[j]=c; }
        dec+=s;
        }
    if (dec=="") dec="0";
    if (sig<0) dec="-"+dec;

    if (i2)
        {
        dec+='.';
        s=hex.SubString(i2,i3-i2+1);
        l=s.Length();
        for (i=1;i<=l;i++)
            {
            c=s[i];
                 if ((c>='0')&&(c<='9')) c-='0';
            else if ((c>='A')&&(c<='F')) c-='A'-10;
            else if ((c>='a')&&(c<='f')) c-='A'-10;
            s[i]=c;
            }
        ll=((l*1234)>>10);  // num of decimals to compute
        for (cy=0,i=1;i<=ll;i++)
            {
            for (cy=0,j=l;j>=1;j--)
                {
                val=s[j];
                val*=10;
                val+=cy;
                s[j]=val&15;
                cy=val>>4;
                }
            dec+=char(cy+'0');
            for (;;)
                {
                if (!l) break;;
                if (s[l]) break;
                l--;
                }
            if (!l) break;;
            }
        }

    return dec;
    }
//---------------------------------------------------------------------------
AnsiString str_dec2hex(AnsiString dec)
    {
    AnsiString hex=""; BYTE a,b;
    int  i,j,i0,i1,i2,i3,l,sig;
    sig=+1; l=dec.Length();
    i0=0; i1=l; i2=0; i3=l;
    for (i=1;i<=l;i++)      // scan for parts of number
        {
        char c=dec[i];
        if (c=='-') sig=-sig;
        if ((c=='.')||(c==',')) i1=i-1;
        if ((c>='0')&&(c<='9')) { if (!i0) i0=i; if ((!i2)&&(i>i1)) i2=i; }
        }
    if (i0) for (;i1>=i0;i1=j-1)// process integer part /16
        {
        for (a=0,j=i0,i=i0;i<=i1;i++)
            {
            a*=10; a+=dec[i]-'0';
            if (a<16) { if (j>i0){ dec[j]='0'; j++; } continue; }
            b=a>>4; a=a&15;
            if (b>10) { dec[j]='1'; j++; b-=10; }
            dec[j]=b+'0'; j++;
            }
        if ((!a)&&(hex=="")) continue;
        if (a<10) a+='0'; else a+='A'-10;
        hex=AnsiString(char(a))+hex;
        }
    if (hex=="") hex="0";

    if ((i2)&&(i2<=i3))     // process fractional part *16
     for (hex+=".",j=i3-i2+2;j;j--)
        {
        for (a=0,b=0,i=i3;i>=i2;i--)
            {
            a=dec[i]-'0';
            b+=a<<4; dec[i]=(b%10)+'0'; b/=10;
            }
        if (b<10) b+='0'; else b+='A'-10;
        hex+=char(b);
        }
    if (sig<0) hex="-"+hex; hex+="h";
    return hex;
    }
//---------------------------------------------------------------------------

顺便说一句,如果您需要截断小数位(以格式化数字),那么必须按被截取部分的最高有效数字四舍五入。

  • 如果数字大于等于5,则在十进制模式下向上舍入
  • 如果数字大于等于8,则在十六进制模式下向上舍入

如果您想知道这行代码的含义:

ll=((l*1234)>>10);  // num of decimals to compute

然后它计算与输入字符串精度匹配的小数位数(每个十六进制小数位1.205个十进制小数位)。我通过对每个数字的分数部分进行高达1280位的经验测量来获得这个比率的准确性。为了简单起见,1e-l可以存储,最大误差为1e-(l+1)。这个比率几乎是恒定的(除了低小数位值(<16位数字)),因此该公式可以安全地用于任何更大的数字位数。在低输入数字值中,输出错误的最大值为1(>8位数字)或2(≤8位数字)位。

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