Difference between revisions of "Base converting"

From Free Pascal wiki
Jump to navigationJump to search
(Created page with " {{Base_converting}} This unit will allow you to switch from one number base to another, each base is chosen ini the range 2..36. <syntaxhighlight> unit BaseConvert; inter...")
 
m (Replace deprecated enclose attributes)
 
(6 intermediate revisions by 3 users not shown)
Line 2: Line 2:
 
{{Base_converting}}
 
{{Base_converting}}
  
This unit will allow you to switch from one number base to another, each base is chosen ini the range 2..36.
+
The following [[Unit|<syntaxhighlight lang="pascal" inline>unit</syntaxhighlight>]] will allow you to convert from one number base to another.
 +
Each base is chosen in the range <syntaxhighlight lang="pascal" inline>2..36</syntaxhighlight>.
  
<syntaxhighlight>
+
Note, the unit does not know a negative sign.
  
unit BaseConvert;
+
== Source ==
 +
<syntaxhighlight lang="pascal" line>
 +
unit baseConvert;
  
 +
// for exceptions
 +
{$mode objfpc}
 +
 +
// P U B L I C ======================================================== //
 
interface
 
interface
 +
 +
const
 +
/// list of symbols for integer representations
 +
digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
 +
 +
function intToBaseStr(value: longword; const base: cardinal = 10): string;
 +
function baseStrToInt(const value: string; const base: cardinal = 10): longword;
  
function IntToBaseStr(Value, BaseOut: integer): String;
+
function baseToBase(
function BaseStrToInt(Value: String; BaseIn: Integer): Integer;  
+
const value: string;
function BaseToBase(ValueIn: String; baseIn, BaseOut: Integer): String;
+
const interpretation: cardinal = 10;
 +
const base: cardinal = 10
 +
): string;
 +
  
 +
// P R I V A T E ====================================================== //
 
implementation
 
implementation
 +
 +
uses
 +
// math unit for inRange and divMod routines
 +
math,
 +
// sysutils unit for ERangeError exception
 +
sysutils;
  
resourceString  
+
resourceString
  rsBadBase = 'Base %d invalide, elle doit être comprise entre'  
+
/// base not within range of available symbols
    +' %d et %d';  
+
errInvalidBase = '↯ b = %0:d! ⇒ b ∉ [%1:d, %2:d]';
  rsBadDigitInValue = 'Mauvais chiffre %s dans la valeur, base = %d';
+
/// supplied character not within domain
 +
errInvalidSymbol = '↯ v(''%0:s'')↑; b = %1:d';
 +
 +
(**
 +
\brief expresses the value in the desired base
 +
 +
\param value the value to convert
 +
\param base
 +
 +
\return a string representing the number in given base
 +
*)
 +
function intToBaseStr(value: longword; const base: cardinal = 10): string;
 +
var
 +
remainder: longword;
 +
begin
 +
// validity of the base
 +
if not inRange(base, 2, length(digits)) then
 +
begin
 +
raise eRangeError.createFmt(errInvalidBase, [base, 2, length(digits)]);
 +
end;
 +
 +
// decomposing the value: reverse Horner-scheme
 +
// The repeat-statement takes the value 0 into account.
 +
result := emptyStr;
 +
repeat
 +
begin
 +
divMod(value, base, value, remainder);
 +
// prepend value: intermediate results are read backwards
 +
result := digits[remainder + 1] + result;
 +
end
 +
until value = 0;
 +
end;
 +
 +
(**
 +
\brief transforms the string of a number
 +
      expressed in the parameter base
 +
      into the equivalent integer
 +
 +
\param value the representation of the integer
 +
\param base assume string is in given base
 +
 +
If the string is empty, the result is 0.
 +
 +
Caution:
 +
No verification of integer overflows.
 +
Reasonable values have to be used.
 +
 +
\return the integer
 +
*)
 +
function baseStrToInt(const value: string; const base: cardinal = 10): longword;
 +
var
 +
symbol: char;
 +
// beware, cardinal is _non-negative_
 +
digit: cardinal;
 +
begin
 +
result := 0;
 +
// Horner-scheme
 +
for symbol in value do
 +
begin
 +
// determine ordinal value of current symbol
 +
digit := pos(symbol, digits);
 +
// If symbol wasn't found, pos returns 0.
 +
// Or possibly the symbol isn't known in the base.
 +
if not inRange(digit, 1, base) then
 +
begin
 +
raise eRangeError.createFmt(errInvalidSymbol, [symbol, base]);
 +
end;
 +
// e.g. the symbol '0' is the _first_ character in digits
 +
// thus the pos function returned 1 => here we subtract one
 +
dec(digit);
 +
result := result * base + digit;
 +
end;
 +
end;
 +
 +
(**
 +
\brief direct conversion of number expressed in one base to another
 +
 +
\param value the value to convert from
 +
\param interpretation the base to interpret the value as
 +
\param base the base to convert to
 +
*)
 +
function baseToBase(
 +
const value: string;
 +
const interpretation: cardinal = 10;
 +
const base: cardinal = 10
 +
): string;
 +
begin
 +
// the case interpretation = base
 +
// intentionally does not have special treatment for unit tests
 +
result := intToBaseStr(baseStrToInt(value, interpretation), base);
 +
end;
  
const
+
// end of unit
  Digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
end.
 +
</syntaxhighlight>
 +
 
 +
== Remarks ==
 +
 
 +
For your information:
 +
For bases two, eight, ten, and sixteen, you can use <code>binStr</code>, <code>OctStr</code>, <code>str</code> and <code>hexStr</code> from the automatically included <code>system</code> unit.
 +
 
 +
== Locales ==
 +
 
 +
<tt>en.po</tt>
 +
<small><syntaxhighlight lang="bash">
 +
#: 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."
 +
</syntaxhighlight></small>
 +
 
 +
<tt>de.po</tt>
 +
<small><syntaxhighlight lang="bash">
 +
#: 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."
 +
</syntaxhighlight></small>
  
// Exprime la valeur dans la base voulue.
+
<tt>fr.po</tt>:
function IntToBaseStr(Value, BaseOut: integer): String;
+
<small><syntaxhighlight lang="bash">
var
+
#: baseconvert:errinvalidbase
  rmndr: integer;  // reste
+
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]"
begin
+
msgstr "Base %0:d invalide, elle doit être comprise entre %1:d et %2:d."
  // validité de la base ?
 
  if not InRange(BaseOut, 2, Length(Digits)) then
 
    raise ERangeError.CreateFmt(rsBadBase, [BaseOut, 2, length(Digits)]);
 
  // Décomposition de la valeur, la variable prend à chaque fois la valeur du diviseur
 
  // L'instruction repeat permet de tenir compte de la valeur 0  
 
  Result := '';
 
  repeat
 
    divmod(Value, BaseOut, Value, rmndr);
 
    Result := Digits[rmndr+1] + Result;
 
  until Value = 0;
 
End;
 
  
// Transforme la chaîne d'un nombre exprimé dans la base paramètre en l'entier équivalent.
+
#: baseconvert:errinvalidsymbol
// Si la chaîne est vide, le résultat est 0.
+
msgid "\342\206\257\302\240v('%0:s')\342\206\221; b\302\240= %1:d"
// Attention : Pas de vérification de dépassement d'entier. il faut utiliser des valeurs raisonnables
+
msgstr "Mauvais chiffre '%0:s' dans la valeur, base = %1:d."
function BaseStrToInt(Value: String; BaseIn: Integer): Integer;
+
</syntaxhighlight></small>
var
 
  d: Char;
 
  digit: Integer;  
 
begin
 
  Result := 0;
 
  for d in Value do
 
  begin
 
    digit := Pos(d, Digits) - 1;
 
    if not InRange(digit, 0, BaseIn-1) then
 
      raise ERangeError.CreateFmt(rsBadDigitInValue, [d, BaseIn]);
 
    Result := Result * BaseIn + digit;
 
  end;
 
End;
 
  
// Conversion directe de nombre exprimé dans une base vers une autre
+
== More ... ==
function BaseToBase(ValueIn: String; baseIn, BaseOut: Integer): String;
 
begin
 
  // le cas BaseIn=BaseOut n'est volontairement pas traité de manière spécifique
 
  // en vue des tests unitaires.  
 
  Result := IntToBaseStr(BaseStrToInt(ValueIn, BaseIn), BaseOut);
 
End;
 
  
End.
+
A more elaborate implementation (based upon the example code above) can be found in [http://svn.code.sf.net/p/flyingsheep/code/trunk/MijnLib/fsiconv.pp this FsiConv unit].<br>
</syntaxhighlight>
+
It has implementations for all integer types (signed and unsigned) as well as TryStrToXXX and StrToXXXDef functions.
<br>
 

Latest revision as of 10:07, 26 May 2022

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

  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 results 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."

More ...

A more elaborate implementation (based upon the example code above) can be found in this FsiConv unit.
It has implementations for all integer types (signed and unsigned) as well as TryStrToXXX and StrToXXXDef functions.