# Difference between revisions of "Base converting"

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

```  1unit baseConvert;
2
3// for exceptions
4{\$mode objfpc}
5
6// P U B L I C ======================================================== //
7interface
8
9const
10	/// list of symbols for integer representations
11	digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
12
13function intToBaseStr(value: longword; const base: cardinal = 10): string;
14function baseStrToInt(const value: string; const base: cardinal = 10): longword;
15
16function 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 ====================================================== //
24implementation
25
26uses
27	// math unit for inRange and divMod routines
28	math,
29	// sysutils unit for ERangeError exception
30	sysutils;
31
32resourceString
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*)
46function intToBaseStr(value: longword; const base: cardinal = 10): string;
47var
48	remainder: longword;
49begin
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 result are read backwards
63		result := digits[remainder + 1] + result;
64	end
65	until value = 0;
66end;
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*)
84function baseStrToInt(const value: string; const base: cardinal = 10): longword;
85var
86	symbol: char;
87	// beware, cardinal is _non-negative_
88	digit: cardinal;
89begin
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;
107end;
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*)
116function baseToBase(
117		const value: string;
118		const interpretation: cardinal = 10;
119		const base: cardinal = 10
120	): string;
121begin
122	// the case interpretation = base
123	// intentionally does not have special treatment for unit tests
124	result := intToBaseStr(baseStrToInt(value, interpretation), base);
125end;
126
127// end of unit
128end.
```

## 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."
```