0

I'm maintenancing an old project with Delphi 7. I need to convert a long hexadecimal string to a decimal string. I searched and found sample code in C# but not in Delphi. I only have two options:

  1. Implement or use a function in Delphi 7.
  2. Implement or use a function in Delphi 2010 then export it as DLL.

Maximum length of hex string I'm working on is 40 character, here is a sample :

 '6F000080B4D4B3426C66A655010001000080B4' 

I used rapidtables to do conversion and here it's output

'2475382888117010136950089026926167642744062132' 

I hope that some one has solved this problem before and can help. Maybe someone give me an algorithm I can use to write a function in Delphi.

Note :
In Delphi 7 for Int64 the maximum positive value is $7FFFFFFFFFFFFFFF = 9223372036854775807 and this value is far from what I need.

6
  • 2
    Did you look at rvelthuis.de/programs/bigintegers.html? Commented Nov 13, 2022 at 1:31
  • Thanks @Ron Maupin, I will install Delphi 2010 and test it. Commented Nov 13, 2022 at 1:44
  • I have used it for IPv6 addresses (32 hexadecimal digits), and it seems to work well. Commented Nov 13, 2022 at 1:51
  • Those are 38 characters, not 40. Also Delphi 7 knows UInt64. Commented Nov 13, 2022 at 17:04
  • @AmigoJack, I know those are 38. 40 character is the maximum length. Yes there is UInt64 in Delphi 7 but it not working as expected for example if u is UInt64 and tried u := $FFFFFFFFFFFFFFFF; you will get error ": Constant expression violates subrange bounds". Even if UInt64 was working it will not solve my problem. Commented Nov 13, 2022 at 18:06

1 Answer 1

3

For this to solve we need to cut it down into three steps:

  1. Converting the hexadecimal number digit by digit. This leads into solving how to
  2. multiply decimal numbers, which in turn needs us being able to
  3. add decimal numbers digit by digit.

As tiny helpers which are needed in the other functions let me have these:

type TArrayString= Array of String; // In case it doesn't exist already // Fill a text with leading zeroes up to the desired length procedure MinLen( var s: String; iLen: Integer ); begin while Length( s )< iLen do s:= '0'+ s; end; 

Addition

We all learnt written addition in school: write all numbers in one row, then add each row's digits and carry that sum's additional digits over to the next row of digits of the summands. This can also be done easily:

// Addition of multiple long numbers function Summe( aSummand: TArrayString ): String; var iLenMax, iA, iPos, iSum, iAdvance: Integer; c: Char; begin result:= '0'; case Length( aSummand ) of 0: exit; // Nothing to add at all 1: begin result:= aSummand[Low( aSummand )]; // Sum equals the only summand exit; end; end; // Find the longest text, then make all texts as long as the longest, // so we can simply access an existing character at that position. iLenMax:= 0; for iA:= Low( aSummand ) to High( aSummand ) do begin if Length( aSummand[iA] )> iLenMax then iLenMax:= Length( aSummand[iA] ); end; for iA:= Low( aSummand ) to High( aSummand ) do MinLen( aSummand[iA], iLenMax ); MinLen( result, iLenMax ); // All have the same length: process from the least significant digit // (right) to the most significant digit (left). for iPos:= iLenMax downto 1 do begin // Manual addition: write all numbers in one row, then add single // digits per row. Nobody will ever give this function so many // summands that the sum of single digits will come near the Integer // capacity. iSum:= 0; for iA:= Low( aSummand ) to High( aSummand ) do begin Inc( iSum, Ord( aSummand[iA][iPos] )- $30 ); // Add digit from each number end; Inc( iSum, Ord( result[iPos] )- $30 ); // Also add result's digit from potential previous carry // Turn that Integer sum into text again, digit by digit iAdvance:= 0; // Exceeding the current position if we need to carry while iSum> 0 do begin c:= Chr( (iSum mod 10)+ $30 ); // Only the rightmost digit if iPos- iAdvance< 1 then begin // Outside the String? result:= c+ result; // Prepend end else begin result[iPos- iAdvance]:= c; // Set new digit in overall sum end; iSum:= iSum div 10; // This digit has been process, go to next one Inc( iAdvance ); // Not in the current position anymore, but processing potential carries end; end; end; 

I did not restrict it to always 2 summands for the following reasons:

  • Dealing with an unknown amount of summands needs virtually no extra work.
  • Adding multiple summands (instead of always 2) in one go is more efficient than calling this function again and again.
  • Later with multiplication we can lazily call this function once with f.e. 6 times the same summand to simulate multiplication by 6.

$30 is the ASCII code for the character '0' - subtracting the potential character '0' to '9' by that of '0' gives us the value 0 to 9.

Multiplication

We all learnt written multiplication in school, too: for each digit of one factor calculate that product (which can only be an "easy" multiplication by 0 to 9), write down all those products in a row as per digit position (optionally with trailing zeroes), then add all those products into a sum (referencing written addition). This can also be done easily, since we now have solved addition already:

// Multiplication of two long numbers function Produkt( s1, s2: String ): String; var iPos, iStep, iA, iNine: Integer; aSummand, aStep: TArrayString; begin // For each digit of one factor we will make a separate multiplication SetLength( aSummand, Length( s1 ) ); // This time it doesn't matter how long both numbers are: just again go // from least significant digit (right) to most significant digit (left). for iPos:= Length( s1 ) downto 1 do begin iA:= Length( s1 )- iPos; // Array index per digit // As per our position the sum must be shifted by 10: first (rightmost) // digit needs no shift (0), second needs one (10), third needs two (100)... MinLen( aSummand[iA], iA ); // Current digit iStep:= Ord( s1[iPos] )- $30; case iStep of 0: ; // Multiplication by 0 always results in 0, an empty summand equals one of "0" 1: aSummand[iA]:= s2+ aSummand[iA]; // No multiplication needed, just prepend with factor else // Cheap multiplication: use addition with 2 to 9 times the same summand SetLength( aStep, iStep ); for iNine:= 0 to iStep- 1 do aStep[iNine]:= s2; aSummand[iA]:= Summe( aStep )+ aSummand[iA]; // Prepend sum, zeroes must be trailing end; end; // Now just add all sums that we got per digit result:= Summe( aSummand ); end; 

It could have been even shorter, since Summe() can already deal with 0 and 1 summands - I don't really need to treat that differently. As previously told: the easy multiplication is done by simple addition - not very performance efficient, but easy to comprehend.

Hexadecimal to decimal conversion

Since we can now add and multiply, we're also able to convert each digit of a non-decimal-base number and increase the outcome to the overall result:

// Convert base 2..36 long number into base 10 long number function ConvertToDecimal( sFrom: String; sBase: String= '16' ): String; var sStep, sPos: String; cFrom: Char; a: TArrayString; begin SetLength( a, 2 ); a[0]:= '0'; // Overall sum = result sPos:= '1'; // Current digit's power while Length( sFrom )> 0 do begin // Process least significant digit (right) cFrom:= sFrom[Length( sFrom )]; case cFrom of // For now at max base 36 is supported, which includes hexadecimal '0'.. '9': sStep:= cFrom; // Same as text 'A'.. 'Z': sStep:= IntToStr( Ord( cFrom )- $41+ 10 ); // Turn "B" into "11" end; a[1]:= Produkt( sPos, sStep ); // Multiply current digit's value by current position's power a[0]:= Summe( a ); // Add both product and current overall result sPos:= Produkt( sPos, sBase ); // Increase power to next digit position Delete( sFrom, Length( sFrom ), 1 ); // Remove rightmost digit end; result:= a[0]; end; 

It even works not only for hexadecimal (base 16) input, but also for others. $41 is the ASCII value for 'A' - subtracting the potential characters 'A' to 'Z' by that of 'A' gives us the value 0 to 25, to which we then just add 10.

Tests

Strings are implied. Spaces are just for optical reasons.

function parameters result proof
Summe() 123 + 456 579 brain
Summe() 123 + 456 + 9999 10578 MS calc, brain
Produkt() 36 * 12 504 MS calc, brain
Produkt() 8 6426578999 * 9731421999 8 4105351216 9179999001 rapidtables.com
ConvertToDecimal() 6F0000 80B4D4B3 426C66A6 55010001 000080B4 247538 2888117010 1369500890 2692616764 2744062132 rapidtables.com

Summary

  • This is a good exercise on turning basic/existing skills (arithmetics) into program code in the sense of needing more understanding in logics than in programming/the language, making it appealing to both beginners (still struggling with programming) and experts (mostly focusing on task automation).
  • Performance wise this is surely far from optimal, but should be rather easy to understand/comprehend/follow instead of being difficult to read. Likewise it should be easy to translate into other languages.
  • A bonus exercise would be to also add subtraction and division.
  • A few tiny details are omitted, f.e. numbers are always expected to not have leading zeroes - the results may then also have unneeded leading zeroes. Empty Strings are interpreted as 0, but a result of 0 is not reduced to an empty String either.
  • It works with any String type, because we only operate on ASCII anyway - there's no need to distinguish between AnsiString, UnicodeString, WideString and so on. However, Char is used as type, which is bound to that choice. But the functions Chr() and Ord() should support everything again.
Sign up to request clarification or add additional context in comments.

1 Comment

I already used "rvelthuis.de/programs/bigintegers.html" as Ron Maupin advised me and exported the function I wan using a DLL. But I accepted @AmigoJack answer since it is the only answer in Delphi 7. A lot of thanks for both of them.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.