0

I'm writing a base converter because I have a test soon and I need to convert a binary number in 3 different bases: octal, decimal and hexadecimal. I've already written the code that convert a binary string into decimal and hexadecimal.

function bintodec(Value:string;dec:TEdit;hexadec:TEdit): Integer;
 var             //dec and hexadec are the TEdits where I will put the result
  i, iValueSize: Integer;
  Edit2,f:TEdit;
begin
  Result := 0;
  iValueSize := Length(Value);
  for i := iValueSize downto 1 do
    begin
      if Value[i] = '1' then Result := Result + (1 shl (iValueSize - i));
    end;
  dec.Text:=(IntToStr(Result));        //dec. number
  hexadec.Text:=(IntToHex(Result,8));  //hexadec. number
end;

As you can see here, the function takes a string (for example 10101001) and puts into 2 different edit the result.

enter image description here

I've made a function that convert a decimal number into an octal number but when I press the SpeedButton Calc. I have an error. It says that project1 raised a class exception 'External: SIGSEGV' and then near to Unit1 I see the page control.inc. I've searched on google a solution but I didn't find useful answers.

function dec2oct(mystring:Integer): String;
  var
  a: String;
  getal_met_rest : Double;
  Edit2:TEdit;
  begin
    while mystring> 0 do
         begin
            getal_met_rest := getal / 8;
            a:= a + IntToStr(mystring - (trunc(getal_met_rest)*8));
            getal := trunc(getal_met_rest);
         end;
    dec2oct:=ReverseString(a);
    Edit2.text:=dec2oct
  end; 

I didn't find a way for binary-octal conversion, so once I've converted from binary to decimal, I call the function dec2oct . I call the functions in this way:

var a:smallint;
begin
 bintodec(Edit1.Text,Edit3,Edit4);
 dec2oct(Edit3.Text); //Edit3 contains the number on base 10
end;

Could you help me?

Argalatyr
  • 4,639
  • 3
  • 36
  • 62
Alberto Rossi
  • 1,800
  • 4
  • 36
  • 58

3 Answers3

3

I would usually use string or character arrays and bit arithmetic for such conversions. For instance:

function Int2Oct(invalue: integer): ShortString;
  const
    tt: array[0..7] of char = ('0', '1', '2', '3', '4', '5', '6', '7');
  var
    tempval: integer;
  begin
    Result := '';
    tempval := invalue;
    if tempval = 0 then
      Result := '0'
    else
      while (tempval <> 0) do
        begin
          Result := tt[(tempval and $7)] + Result;
          tempval := (tempval shr 3);
        end;
  end;

Seems to work in the short little bit of time that I tested it as long as you don't expect it to handle negative numbers. Edit: It handles zero now.

Glenn1234
  • 2,542
  • 1
  • 16
  • 21
  • @DavidHeffernan Agreed and I found that out about 20 mins ago. Easily fixed though by checking for zero before the loop and setting the value. I'll edit for that. – Glenn1234 May 27 '13 at 21:13
  • Put the zero check first and exit immediately. No point assigning '' and then changing your mind later – David Heffernan May 27 '13 at 21:18
3

Here's a function that works somewhat like the C runtime library's itoa function, converting a positive integer value (Cardinal in Delphi) to a specified radix between 2 and 36. It's been tested under Delphi 2007 and XE4.

type
  TRadixRange = 2..36;

function ConvertIntToBase(value : Cardinal; Radix : TRadixRange) : string;
const
  Digits: array[0..35] of Char = ('0', '1', '2', '3',
                                  '4', '5', '6', '7',
                                  '8', '9', 'A', 'B',
                                  'C', 'D', 'E', 'F',
                                  'G', 'H', 'I', 'J',
                                  'K', 'L', 'M', 'N',
                                  'O', 'P', 'Q', 'R',
                                  'S', 'T', 'U', 'V',
                                  'W', 'X', 'Y', 'Z');
var
  nIndex : Integer;
begin
  Result := '';
  repeat
    nIndex := value mod radix;
    Result := Digits[nIndex] + Result;
    Value := Value div radix;
  until Value = 0;
end;

Just for fun, I decided to write a function to "undo" the conversion (convert from another radix back to decimal (base 10)). It's also modeled (very loosely) after the C RTL function atoi, except it requires you to pass in the radix of the number being passed.

function ConvertBaseToInt(const Value: string; const Radix: TRadixRange): Cardinal;
var
  i: Integer;
  Increment: Byte;
begin
  Result := 0;
  for i := 1 to Length(Value) do
  begin
    case Value[i] of
      '0'..'9': Increment := Ord(Value[i]) - Ord('0');
      'A'..'Z',
      'a'..'z': Increment := Ord(Value[i]) - Ord('A') + 10;
    else
      Increment := 0;
    end;
  end;
  Result := Result * Radix + Increment;
end;

Note that ConvertIntToBase was tested with many numeric inputs, but I can only validate those bases supported by Windows Calculator in programmer mode (binary (base 2), octal (base 8), decimal (base 10, which I did not test), and hex (base 16)), as I don't have a calculator that will support other radix values and didn't want to do the work by hand. ;-)

ConvertBaseToInt was tested by passing in the test values of ConvertIntToBase and confirming that what went into one was what came back out of the other; IOW, that a number converted into binary by ConvertIntToBase would result in the same number when run back through ConvertBaseToInt.

You can test it with something similar to this in a console application:

var
  TempStr: string;
  Reversed: Integer;
  i: Integer;
  Base: Byte;
const
  FmtStr = 'Value (base %d): %d  %s and back %d';
begin
  for i := 0 to 16 do
  begin
    for Base in [2, 8, 16] do  
    begin
      // Test bin, oct, and hex for a range of values from 0..65536
      TempStr := ConvertIntToBase(1 shl i, Base);
      Reversed := ConvertBaseToInt(TempStr, Base);
      Writeln(Format(FmtStr, [Base, 1 shl i, TempStr, Reversed]));
    end;
  end;
  Readln;
end.
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • +1 I like this. No reliance on powers of two or shifting. Proper handling of 0. – David Heffernan May 28 '13 at 06:53
  • +1, thank you Ken you answer was very useful. I'd like ask you a thing: I'm using the function StrToHex(myvalue,8) as converter. Is your way faster than mine? – Alberto Rossi May 28 '13 at 11:04
  • 1
    @Alberto, I have no idea. I've never benchmarked them, and mine converts numbers to string, not string to number. The advantage of mine is that it converts any cardinal number to any base with a single function call; you don't have to maintain separate ones. – Ken White May 28 '13 at 11:25
  • @DavidHeffernan: Thanks. Hope you like the additions as well. :-) – Ken White May 28 '13 at 20:12
2
program project1;

uses
    SysUtils, StrUtils;

begin
    // StrToInt function supports constants syntax: 
    // & - octal notation
    // $ - hexadecimal notation
    // % - binary notation
    Writeln(StrToInt('123'));
    Writeln(StrToInt('&173'))
    Writeln(StrToInt('$7B'));
    Writeln(StrToInt('%01111011'));

    // There are three functions for converting integer value to decimal, hexadecimal and binary notation
    Writeln(IntToStr(123));
    Writeln(IntToHex(123, 2));
    Writeln(intToBin(123, 8));

    Readln;
end.

For other bases answer of Ken White is very usefull.

But in the unit StrUtils such functions already exists:

Dec2Numb
Synopsis: Convert a decimal number to a string representation, using given a base.
Declaration: function Dec2Numb(N: LongInt;Len: Byte;Base: Byte) : string
Visibility: default
Description: Dec2Numb converts N to its representation using base Base. The resulting string is left-padded
with zeroes till it has length Len. Base must be in the range 2-36 to be meaningful, but no checking
on this is performed.
Errors: If Base is out of range, the resulting string will contain unreadable (non-alphanumeric) characters.


Numb2Dec
Synopsis: Converts a string representation of a number to its numerical value, given a certain base.
Declaration: function Numb2Dec(S: string;Base: Byte) : LongInt
Visibility: default
Description: Numb2Dec converts the number in string S to a decimal value. It assumes the number is represented
using Base as the base. No checking is performed to see whether S contains a valid number using
base Base.
Errors: None.
Abelisto
  • 14,826
  • 2
  • 33
  • 41