Convert a "big" Hex number (string format) to a decimal number (string format) without BigInteger Class

StackOverflow https://stackoverflow.com/questions/16965915

  •  31-05-2022
  •  | 
  •  

Question

How to convert a "big" Hex number (in string format):

EC851A69B8ACD843164E10CFF70CF9E86DC2FEE3CF6F374B43C854E3342A2F1AC3E30C741CC41E679DF6D07CE6FA3A66083EC9B8C8BF3AF05D8BDBB0AA6CB3EF8C5BAA2A5E531BA9E28592F99E0FE4F95169A6C63F635D0197E325C5EC76219B907E4EBDCD401FB1986E4E3CA661FF73E7E2B8FD9988E753B7042B2BBCA76679

to a decimal number (in string format):

166089946137986168535368849184301740204613753693156360462575217560130904921953976324839782808018277000296027060873747803291797869684516494894741699267674246881622658654267131250470956587908385447044319923040838072975636163137212887824248575510341104029461758594855159174329892125993844566497176102668262139513

without using BigInteger Class (as my application should support machines without .NET Framework 4)?

Was it helpful?

Solution

Here's a quick-and-dirty implementation that can work with arbitrarily-large numbers. The aim of this implementation is simplicity, not performance; thus, it should be optimized drastically if it's to be used in a production scenario.

Edit: Simplified further per Dan Byström's implementation of the inverse decimal-to-hex conversion:

static string HexToDecimal(string hex)
{
    List<int> dec = new List<int> { 0 };   // decimal result

    foreach (char c in hex)
    {
        int carry = Convert.ToInt32(c.ToString(), 16);   
            // initially holds decimal value of current hex digit;
            // subsequently holds carry-over for multiplication

        for (int i = 0; i < dec.Count; ++i)
        {
            int val = dec[i] * 16 + carry;
            dec[i] = val % 10;
            carry = val / 10;
        }

        while (carry > 0)
        {
            dec.Add(carry % 10);
            carry /= 10;
        }
    }

    var chars = dec.Select(d => (char)('0' + d));
    var cArr = chars.Reverse().ToArray();
    return new string(cArr);
}

OTHER TIPS

I just translated Douglas' code into VBA

Function HexToDecimal(ByVal sHex As String) As String

    Dim dec() As Long
    ReDim dec(0 To 0) As Long

    Dim lCharLoop As Long
    For lCharLoop = 1 To Len(sHex)

        Dim char As String * 1
        char = Mid$(sHex, lCharLoop, 1)

        Dim carry As Long
        carry = Val("&h" & char)

        Dim i As Long
        For i = 0 To UBound(dec)
            Dim lVal As Long
            lVal = dec(i) * 16 + carry
            dec(i) = lVal Mod 10
            carry = lVal \ 10
        Next i

        While (carry > 0)
            ReDim Preserve dec(0 To UBound(dec) + 1) As Long
            dec(UBound(dec)) = carry Mod 10
            carry = carry \ 10
        Wend
    Next

    For lCharLoop = UBound(dec) To LBound(dec) Step -1
        Dim sDecimal As String
        sDecimal = sDecimal & Chr$(48 + dec(lCharLoop))

    Next

    HexToDecimal = sDecimal

End Function

Private Sub TestHexToDecimal()

    Debug.Assert HexToDecimal("F") = "15"
    Debug.Assert HexToDecimal("4") = CStr(Val("&H4"))
    Debug.Assert HexToDecimal("10") = CStr(Val("&H10"))
    Debug.Assert HexToDecimal("20") = CStr(Val("&H20"))
    Debug.Assert HexToDecimal("30") = CStr(Val("&H30"))
    Debug.Assert HexToDecimal("40") = CStr(Val("&H40"))
    Debug.Assert HexToDecimal("44") = CStr(Val("&H44"))
    Debug.Assert HexToDecimal("FF") = "255"
    Debug.Assert HexToDecimal("FFF") = "4095"
    Debug.Assert HexToDecimal("443") = CStr(Val("&H443"))
    Debug.Assert HexToDecimal("443C1") = "279489"
    Debug.Assert HexToDecimal("443C1CE20DFD592FB374D829B894BBE5") = "90699627342249584016268008583970733029"

    Debug.Assert HexToDecimal("EC851A69B8ACD843164E10CFF70CF9E86DC2FEE3CF6F374B43C854E3342A2F1AC3E30" & _
    "C741CC41E679DF6D07CE6FA3A66083EC9B8C8BF3AF05D8BDBB0AA6CB3EF8C5BAA2A5" & _
    "E531BA9E28592F99E0FE4F95169A6C63F635D0197E325C5EC76219B907E4EBDCD401FB1" & _
    "986E4E3CA661FF73E7E2B8FD9988E753B7042B2BBCA76679") = _
    "1660899461379861685353688491843017402046137536931563604625752175601309049219" & _
    "5397632483978280801827700029602706087374780329179786968451649489474169926767" & _
    "4246881622658654267131250470956587908385447044319923040838072975636163137212" & _
    "8878242485755103411040294617585948551591743298921259938445664971761026682621" & _
    "39513"

End Sub

Also a benchmark at statman.info Hexadecimal Conversion for large numbers

You can use the IntX library as it should work with .Net 2.0 and up. From the description on the page in regards to BigInteger:

So internally System.Numerics.BigInteger seems to use standard arbitrary arithmetic algorithms and I am not worrying about IntX library since, due to its use of FHT, it can be times faster for really big integers.

The license is pretty liberal but worth reading first just to make sure it's okay.

I've not used this library but from a cursory glance at the source code this should be all you need to do

string dec = new IntX(myHex, 16).ToString();

If you don't want to compile the code yourself, you can install it via Nuget.

An easy way would be to use a big number library that supports your version of .NET. I'd recommend GnuMpDotNet, which uses the excellent GMP library. By default it targets .NET 3.5, but you can change that to .NET 2.0 without breaking anything (just remove the references and using statement that refer to new things), as it doesn't use anything from .NET 3.5. Here is an example using GnuMpDotNet:

BigInt e = new BigInt(hexString, 16);
string decimalStr = e.ToString();

Look at my answer here: https://stackoverflow.com/a/18231860/2521214

worth looking

  • string based conversions (limited by free memory only)
  • dec->hex and hex<-dec included
  • no bigint/bigreal lib used
  • supporting fixed point string formats (no exponents)

I just translated Douglas code to PHP:

function BigNumberHexToDecimal($hex)
{
    $dec = array(0);
    $hexLen = strlen($hex);
    for($h=0;$h<$hexLen;++$h)
    {
        $carry = hexdec($hex[$h]);
        for ($i = 0; $i < count($dec); ++$i)
        {
            $val = $dec[$i] * 16 + $carry;
            $dec[$i] = $val % 10;
            $carry = (int)($val / 10);
        }
        while ($carry > 0)
        {
            $dec[] = $carry % 10;
            $carry = (int)($carry / 10);
        }
    }

    return join("", array_reverse($dec));
}

I just translated Douglas code to JAVA:

public static String HexToDec(String hex) {
    List<Integer> dec = new ArrayList<Integer>();

    for (int k = 0; k < hex.length(); k++) {
        String c = hex.charAt(k) + "";
        int carry = Integer.parseInt(c, 16);

        for (int i = 0; i < dec.size(); ++i) {
            int val = dec.get(i) * 16 + carry;
            dec.set(i, val % 10);
            carry = val / 10;
        }

        while (carry > 0) {
            dec.add(carry % 10);
            carry /= 10;
        }
    }

    int[] out = new int[dec.size()];
    for (int i = 0; i < dec.size(); i++) {
        out[i] = dec.get(i).intValue();
    }

    return arrayToDecString(reverseArray(out));

}

public static String arrayToDecString(int[] data) {

    String str = "";
    for (int i = 0; i < data.length; i++) {
        str += data[i] + "";
    }
    return str;
}

public static int[] reverseArray(int[] data) {
    for (int i = 0; i < data.length / 2; i++) {
        int temp = data[i];
        data[i] = data[data.length - i - 1];
        data[data.length - i - 1] = temp;
    }
    return data;
}

I just translated Douglas code to Delphi/Pascal:

function HexToDecimal(const Hex: string): string;
var
  dec: TList;
  I: Integer;
  carry: Cardinal;
  c: Char;
  val: Integer;
begin
  Result := '';
  dec := TList.Create;
  try
    dec.Add(Pointer(0));          // decimal result

    for c in Hex do begin
      carry := StrToInt('$' + c); // initially holds decimal value of current hex digit;
                                  // subsequently holds carry-over for multiplication
      for I := 0 to dec.Count -1 do begin
        val := Integer(dec[I]) * 16 + carry;
        dec[I] := Pointer(Integer(val mod 10));
        carry := val div 10;
      end;

      while carry > 0 do begin
        dec.Add(Pointer(Integer(carry mod 10)));
        carry := carry div 10;
      end;
    end;

    for I := 0 to dec.Count -1 do begin
      val := Integer(dec[I]);
      Result := IntToStr(val) + Result;
    end;
  finally
    dec.Free;
  end;
end;

procedure Test;
var
  S: string;
begin
  S := HexToDecimal('FF'); // 255
  S := HexToDecimal('FFF'); // 4095
  S := HexToDecimal('443C1'); // 279489
  S := HexToDecimal('443C1CE20DFD592FB374D829B894BBE5'); // "90699627342249584016268008583970733029"
  S := 'EC851A69B8ACD843164E10CFF70CF9E86DC2FEE3CF6F374B43C854E3342A2F1AC3E30' +
    'C741CC41E679DF6D07CE6FA3A66083EC9B8C8BF3AF05D8BDBB0AA6CB3EF8C5BAA2A5' +
    'E531BA9E28592F99E0FE4F95169A6C63F635D0197E325C5EC76219B907E4EBDCD401FB1' +
    '986E4E3CA661FF73E7E2B8FD9988E753B7042B2BBCA76679';
  S := HexToDecimal(S); // "166089946137986168535368849184301740204613753693156360462575217560130904921953976324839782808018277000296027060873747803291797869684516494894741699267674246881622658654267131250470956587908385447044319923040838072975636163137212887824248575510341104029461758594855159174329892125993844566497176102668262139513"
end;

Translated Douglas code to Qt:

QByteArray convertHexToDecimal(const QByteArray &hex)
{
    QList<int> dec;
    for (int i = 0; i < hex.count(); i++) {
        int carry = hex.mid(i, 1).toInt(nullptr, 16);
        for (int j = 0; j < dec.count(); ++j) {
            int val = dec[j] * 16 + carry;
            dec[j] = val % 10;
            carry = val / 10;
        }

        while (carry > 0) {
            dec.append(carry % 10);
            carry /= 10;
        }
    }

    QByteArray chars;
    foreach (int d, dec) {
        chars.prepend((char)('0' + d));
    }

    return chars;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top