Sign in Join
Wiki copytemporary

Base converting: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Base converting

OldNewLine
1+The following [[Unit|unit]] will allow you to convert from one number base to another. Each base is chosen in the range `2..36`.
2+
3+Note, the unit does not know a negative sign.
4+
5+## Source
6+
7+``` pascal
8+unit baseConvert;
9+
10+// for exceptions
11+{$mode objfpc}
12+
13+// P U B L I C ======================================================== //
14+interface
15+
16+const
17+ /// list of symbols for integer representations
18+ digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
19+
20+function intToBaseStr(value: longword; const base: cardinal = 10): string;
21+function baseStrToInt(const value: string; const base: cardinal = 10): longword;
22+
23+function baseToBase(
24+ const value: string;
25+ const interpretation: cardinal = 10;
26+ const base: cardinal = 10
27+ ): string;
28+
29+
30+// P R I V A T E ====================================================== //
31+implementation
32+
33+uses
34+ // math unit for inRange and divMod routines
35+ math,
36+ // sysutils unit for ERangeError exception
37+ sysutils;
38+
39+resourceString
40+ /// base not within range of available symbols
41+ errInvalidBase = '↯ b = %0:d! ⇒ b ∉ [%1:d, %2:d]';
42+ /// supplied character not within domain
43+ errInvalidSymbol = '↯ v(''%0:s'')↑; b = %1:d';
44+
45+(**
46+ \brief expresses the value in the desired base
47+
48+ \param value the value to convert
49+ \param base
50+
51+ \return a string representing the number in given base
52+*)
53+function intToBaseStr(value: longword; const base: cardinal = 10): string;
54+var
55+ remainder: longword;
56+begin
57+ // validity of the base
58+ if not inRange(base, 2, length(digits)) then
59+ begin
60+ raise eRangeError.createFmt(errInvalidBase, [base, 2, length(digits)]);
61+ end;
62+
63+ // decomposing the value: reverse Horner-scheme
64+ // The repeat-statement takes the value 0 into account.
65+ result := emptyStr;
66+ repeat
67+ begin
68+ divMod(value, base, value, remainder);
69+ // prepend value: intermediate results are read backwards
70+ result := digits[remainder + 1] + result;
71+ end
72+ until value = 0;
73+end;
74+
75+(**
76+ \brief transforms the string of a number
77+ expressed in the parameter base
78+ into the equivalent integer
79+
80+ \param value the representation of the integer
81+ \param base assume string is in given base
82+
83+ If the string is empty, the result is 0.
84+
85+ Caution:
86+ No verification of integer overflows.
87+ Reasonable values have to be used.
88+
89+ \return the integer
90+*)
91+function baseStrToInt(const value: string; const base: cardinal = 10): longword;
92+var
93+ symbol: char;
94+ // beware, cardinal is _non-negative_
95+ digit: cardinal;
96+begin
97+ result := 0;
98+ // Horner-scheme
99+ for symbol in value do
100+ begin
101+ // determine ordinal value of current symbol
102+ digit := pos(symbol, digits);
103+ // If symbol wasn't found, pos returns 0.
104+ // Or possibly the symbol isn't known in the base.
105+ if not inRange(digit, 1, base) then
106+ begin
107+ raise eRangeError.createFmt(errInvalidSymbol, [symbol, base]);
108+ end;
109+ // e.g. the symbol '0' is the _first_ character in digits
110+ // thus the pos function returned 1 => here we subtract one
111+ dec(digit);
112+ result := result * base + digit;
113+ end;
114+end;
115+
116+(**
117+ \brief direct conversion of number expressed in one base to another
118+
119+ \param value the value to convert from
120+ \param interpretation the base to interpret the value as
121+ \param base the base to convert to
122+*)
123+function baseToBase(
124+ const value: string;
125+ const interpretation: cardinal = 10;
126+ const base: cardinal = 10
127+ ): string;
128+begin
129+ // the case interpretation = base
130+ // intentionally does not have special treatment for unit tests
131+ result := intToBaseStr(baseStrToInt(value, interpretation), base);
132+end;
133+
134+// end of unit
135+end.
136+```
137+
138+## Remarks
139+
140+For your information: For bases two, eight, ten, and sixteen, you can use `binStr`, `OctStr`, `str` and `hexStr` from the automatically included `system` unit.
141+
142+## Locales
143+
144+`en.po` <small>
145+
146+``` bash
147+#: baseconvert:errinvalidbase
148+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]"
149+msgstr "Base %0:d is invalid. It must be between %1:d and %2:d."
150+
151+#: baseconvert:errinvalidsymbol
152+msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
153+msgstr "The symbol '%0:s' has an unacceptable value for base = %1:d."
154+```
155+
156+</small>
157+
158+`de.po` <small>
159+
160+``` bash
161+#: baseconvert:errinvalidbase
162+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]"
163+msgstr "Die Basis %0:d ist ungültig. Sie muß zwischen %1:d und %2:d sein."
164+
165+#: baseconvert:errinvalidsymbol
166+msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
167+msgstr "Der Zeichenwert von '%0:s' ist ungültig für Zahlen der Basis %1:d."
168+```
169+
170+</small>
171+
172+`fr.po`: <small>
173+
174+``` bash
175+#: baseconvert:errinvalidbase
176+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]"
177+msgstr "Base %0:d invalide, elle doit être comprise entre %1:d et %2:d."
178+
179+#: baseconvert:errinvalidsymbol
180+msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
181+msgstr "Mauvais chiffre '%0:s' dans la valeur, base = %1:d."
182+```
183+
184+</small>
185+
186+## More ...
187+
188+A more elaborate implementation (based upon the example code above) can be found in [this FsiConv unit](http://svn.code.sf.net/p/flyingsheep/code/trunk/MijnLib/fsiconv.pp).\
189+It has implementations for all integer types (signed and unsigned) as well as TryStrToXXX and StrToXXXDef functions.
190+
191+---
192+*From the Free Pascal wiki, page "Base converting", as it stood on 2022-05-26 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history