2

Is there a simple way of printing hexadecimal numbers with leading zeros in Ada? The following will print a number with leading zeros in hex but it is a bit convoluted.

with Interfaces;
with Ada.Strings;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;

procedure A59hex is
   package MIO is new Ada.Text_IO.Modular_IO (Interfaces.Unsigned_32);

   function Hex (hval : Interfaces.Unsigned_32; num_digits : Integer := 8)
      return String
   is
      --  Add 4 for the 16#...#
      hexstr : String (1 .. num_digits + 4);
   begin
      --  Convert to 16#...#
      MIO.Put (To => hexstr, Item => hval, Base => 16);
      declare
         --  String is right justified - trim the hex string
         thex : constant String := Trim (hexstr, Ada.Strings.Left);
         --  Remove the 16# header and # footer
         phex : constant String := thex (thex'First + 3 .. thex'Last - 1);
      begin
         --  Add leading zeros
         return (Tail (phex, num_digits, '0'));
      end;
   end Hex;

begin
   Put (Hex (16#1A#, 4));
   for ii in 0 .. 15 loop
      Put (" " & Hex (Interfaces.Unsigned_32 (ii), 2));
   end loop;
   New_Line;
   Put_Line (Hex (16#7F1B#));
   Put_Line (Hex (16#7FFF1C#));
   Put_Line (Hex (16#7FFFFF1D#));
   Put_Line (Hex (16#9FFFFF1E#));
end A59hex;

Output

001A 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00007F1B
007FFF1C
7FFFFF1D
9FFFFF1E

It creates a string with 16#...#, then removes it, remove the leading spaces and then pads the leading spaces with 0s. Is there a library routine that does what Hex does for 8, 16 and 32 bit unsigned numbers?

4
  • This example may be relevant. Commented Feb 28 at 14:05
  • @trashgod I am aware of that example - I've done something like that in the past. It is more efficient than the putting as a string. I was just wondering if there is an existing library that does the same thing Commented Feb 28 at 17:25
  • It looks like GNAT's System.Image_B generalizes the same approach. Commented Feb 28 at 18:52
  • Just tried System.Image_B - gives the same result except it is left justified and indicates how many characters the resulting string is. No leading zeros though. Commented Mar 1 at 11:09

3 Answers 3

2

Here, a more general - and actually generic! - solution:

with Interfaces;
with Ada.IO_Exceptions, Ada.Text_IO;

procedure A59hex is

   generic
      type T is mod <>;
   function Hex_Any (hval : T; num_digits : Integer := T'size / 4)
      return String;

   function Hex_Any (hval : T; num_digits : Integer := T'size / 4)
      return String
   is
      x : T := hval;
      hex_str : String (1 .. num_digits);
      hex_digit : constant array (T range 0 .. 15) of Character := "0123456789ABCDEF";
   begin
      for i in reverse hex_str'Range loop
         hex_str (i) := hex_digit (x and 15);
         x := x / 16;
      end loop;
      if x /= 0 then
         raise Ada.IO_Exceptions.Layout_Error
            with "Number exceeds digits available with `num_digits`";
      end if;
      return hex_str;
   end Hex_Any;

   function Hex is new Hex_Any (Interfaces.Unsigned_32);

   use Ada.Text_IO;
   
begin
   Put (Hex (16#1A#, 4));
   for ii in Interfaces.Unsigned_32 range 0 .. 15 loop
      Put (" " & Hex (ii, 2));
   end loop;
   New_Line;
   Put_Line (Hex (16#7F1B#));
   Put_Line (Hex (16#7FFF1C#));
   Put_Line (Hex (16#7FFFFF1D#));
   Put_Line (Hex (16#9FFFFF1E#));
end A59hex;
Sign up to request clarification or add additional context in comments.

4 Comments

Pity you can't use shift_right(x, 4) instead of x/16. If you use constant (0..15) of Character instead of String, there is no need to add 1 to the hex_digit index.
The absense of Shift_* outside of Interfaces is a shame. However, GNAT optimizes the /(2**n)'s. You could use array (0 .. 15) of Character instead of a String. Good points!
- edited to "array (0 .. 15) of Character".
Even better: array (T range 0 .. 15) of Character
1

For instance:

with Interfaces;
with Ada.Text_IO;

procedure A59hex is

   function Hex (hval : Interfaces.Unsigned_32; num_digits : Integer := 8)
      return String
   is
     type U64 is mod 2 ** 64;
     package MIO is new Ada.Text_IO.Modular_IO (U64);
     hexstr : String (1 .. 13);
   begin
     MIO.Put (hexstr, U64 (hval) + 16#1_0000_0000#, 16);
     return hexstr (13 - num_digits .. 12);
   end Hex;

   use Ada.Text_IO;
 
begin
   Put (Hex (16#1A#, 4));
   for ii in Interfaces.Unsigned_32 range 0 .. 15 loop
      Put (" " & Hex (ii, 2));
   end loop;
   New_Line;
   Put_Line (Hex (16#7F1B#));
   Put_Line (Hex (16#7FFF1C#));
   Put_Line (Hex (16#7FFFFF1D#));
   Put_Line (Hex (16#9FFFFF1E#));
end A59hex;

1 Comment

Good solution. This is the way I used to do it in MS-Basic. This will only work up to Interfaces.Unsigned_64. It won't work for Interfaces.Unsigned_128
1

I would use an instance of PragmARC.Images.Modular_Image. It works for any modular type, and for any base in 2 .. 16.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.