Base converting

From Free Pascal wiki

English (en) français (fr)

The following unit will allow you to convert from one number base to another. Each base is chosen in the range 2..36.

Note, the unit does not know a negative sign.

source

  1 unit baseConvert;
  2 
  3 // for exceptions
  4 {$mode objfpc}
  5 
  6 // P U B L I C ======================================================== //
  7 interface
  8  
  9 const
 10 	/// list of symbols for integer representations
 11 	digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
 12 
 13 function intToBaseStr(value: longword; const base: cardinal = 10): string;
 14 function baseStrToInt(const value: string; const base: cardinal = 10): longword;
 15 
 16 function baseToBase(
 17 		const value: string;
 18 		const interpretation: cardinal = 10;
 19 		const base: cardinal = 10
 20 	): string;
 21  
 22 
 23 // P R I V A T E ====================================================== //
 24 implementation
 25  
 26 uses
 27 	// math unit for inRange and divMod routines
 28 	math,
 29 	// sysutils unit for ERangeError exception
 30 	sysutils;
 31 
 32 resourceString
 33 	/// base not within range of available symbols
 34 	errInvalidBase = '↯ b = %0:d! ⇒ b ∉ [%1:d, %2:d]';
 35 	/// supplied character not within domain
 36 	errInvalidSymbol = '↯ v(''%0:s'')↑; b = %1:d';
 37  
 38 (**
 39 	\brief expresses the value in the desired base
 40 	
 41 	\param value the value to convert
 42 	\param base
 43 	
 44 	\return a string representing the number in given base
 45 *)
 46 function intToBaseStr(value: longword; const base: cardinal = 10): string;
 47 var
 48 	remainder: longword;
 49 begin
 50 	// validity of the base
 51 	if not inRange(base, 2, length(digits)) then
 52 	begin
 53 		raise eRangeError.createFmt(errInvalidBase, [base, 2, length(digits)]);
 54 	end;
 55 	
 56 	// decomposing the value: reverse Horner-scheme
 57 	// The repeat-statement takes the value 0 into account.
 58 	result := emptyStr;
 59 	repeat
 60 	begin
 61 		divMod(value, base, value, remainder);
 62 		// prepend value: intermediate results are read backwards
 63 		result := digits[remainder + 1] + result;
 64 	end
 65 	until value = 0;
 66 end;
 67  
 68 (**
 69 	\brief transforms the string of a number
 70 	       expressed in the parameter base
 71 	       into the equivalent integer
 72 	
 73 	\param value the representation of the integer
 74 	\param base assume string is in given base
 75 	
 76 	If the string is empty, the result is 0.
 77 	
 78 	Caution:
 79 	No verification of integer overflows.
 80 	Reasonable values have to be used.
 81 	
 82 	\return the integer
 83 *)
 84 function baseStrToInt(const value: string; const base: cardinal = 10): longword;
 85 var
 86 	symbol: char;
 87 	// beware, cardinal is _non-negative_
 88 	digit: cardinal;
 89 begin
 90 	result := 0;
 91 	// Horner-scheme
 92 	for symbol in value do
 93 	begin
 94 		// determine ordinal value of current symbol
 95 		digit := pos(symbol, digits);
 96 		// If symbol wasn't found, pos returns 0.
 97 		// Or possibly the symbol isn't known in the base.
 98 		if not inRange(digit, 1, base) then
 99 		begin
100 			raise eRangeError.createFmt(errInvalidSymbol, [symbol, base]);
101 		end;
102 		// e.g. the symbol '0' is the _first_ character in digits
103 		// thus the pos function returned 1 => here we subtract one
104 		dec(digit);
105 		result := result * base + digit;
106 	end;
107 end;
108  
109 (**
110 	\brief direct conversion of number expressed in one base to another
111 	
112 	\param value the value to convert from
113 	\param interpretation the base to interpret the value as
114 	\param base the base to convert to
115 *)
116 function baseToBase(
117 		const value: string;
118 		const interpretation: cardinal = 10;
119 		const base: cardinal = 10
120 	): string;
121 begin
122 	// the case interpretation = base
123 	// intentionally does not have special treatment for unit tests
124 	result := intToBaseStr(baseStrToInt(value, interpretation), base);
125 end;
126 
127 // end of unit
128 end.

remarks

For your information: For bases two, eight, ten, and sixteen, you can use binStr, OctStr, str and hexStr from the automatically included system unit.

locales

en.po

#: baseconvert:errinvalidbase
msgid "\342\206\257\302\240b\302\240= %0:d!\302\240\342\207\222 b\302\240\342\210\211\302\240[%1:d, %2:d]"
msgstr "Base %0:d is invalid. It must be between %1:d and %2:d."

#: baseconvert:errinvalidsymbol
msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
msgstr "The symbol '%0:s' has an unacceptable value for base = %1:d."

de.po

#: baseconvert:errinvalidbase
msgid "\342\206\257\302\240b\302\240= %0:d!\302\240\342\207\222 b\302\240\342\210\211\302\240[%1:d, %2:d]"
msgstr "Die Basis %0:d ist ungültig. Sie muß zwischen %1:d und %2:d sein."

#: baseconvert:errinvalidsymbol
msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
msgstr "Der Zeichenwert von '%0:s' ist ungültig für Zahlen der Basis %1:d."

fr.po:

#: baseconvert:errinvalidbase
msgid "\342\206\257\302\240b\302\240= %0:d!\302\240\342\207\222 b\302\240\342\210\211\302\240[%1:d, %2:d]"
msgstr "Base %0:d invalide, elle doit être comprise entre %1:d et %2:d."

#: baseconvert:errinvalidsymbol
msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
msgstr "Mauvais chiffre '%0:s' dans la valeur, base = %1:d."