home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / adav313.zip / gnat-3_13p-os2-bin-20010916.zip / emx / gnatlib / s-imgdec.adb < prev    next >
Text File  |  2000-07-19  |  12KB  |  360 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUNTIME COMPONENTS                          --
  4. --                                                                          --
  5. --                        S Y S T E M . I M G _ D E C                       --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.15 $                             --
  10. --                                                                          --
  11. --          Copyright (C) 1992-1997 Free Software Foundation, Inc.          --
  12. --                                                                          --
  13. -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  14. -- terms of the  GNU General Public License as published  by the Free Soft- --
  15. -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
  16. -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  17. -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  18. -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  19. -- for  more details.  You should have  received  a copy of the GNU General --
  20. -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
  21. -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
  22. -- MA 02111-1307, USA.                                                      --
  23. --                                                                          --
  24. -- As a special exception,  if other files  instantiate  generics from this --
  25. -- unit, or you link  this unit with other files  to produce an executable, --
  26. -- this  unit  does not  by itself cause  the resulting  executable  to  be --
  27. -- covered  by the  GNU  General  Public  License.  This exception does not --
  28. -- however invalidate  any other reasons why  the executable file  might be --
  29. -- covered by the  GNU Public License.                                      --
  30. --                                                                          --
  31. -- GNAT was originally developed  by the GNAT team at  New York University. --
  32. -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
  33. --                                                                          --
  34. ------------------------------------------------------------------------------
  35.  
  36. with System.Img_Int; use System.Img_Int;
  37.  
  38. package body System.Img_Dec is
  39.  
  40.    -------------------
  41.    -- Image_Decimal --
  42.    -------------------
  43.  
  44.    function Image_Decimal
  45.      (V     : Integer;
  46.       Scale : Integer)
  47.       return  String
  48.    is
  49.       P : Natural := 0;
  50.       S : String (1 .. 64);
  51.  
  52.    begin
  53.       Set_Image_Decimal (V, S, P, Scale, 1, Integer'Max (1, Scale), 0);
  54.  
  55.       --  Mess around to make sure we have the objectionable space at the
  56.       --  start for positive numbers in accordance with the annoying rules!
  57.  
  58.       if S (1) /= ' ' and then S (1) /= '-' then
  59.          S (2 .. P + 1) := S (1 .. P);
  60.          S (1) := ' ';
  61.          return S (1 .. P + 1);
  62.       else
  63.          return S (1 .. P);
  64.       end if;
  65.    end Image_Decimal;
  66.  
  67.    -----------------------
  68.    -- Set_Image_Decimal --
  69.    -----------------------
  70.  
  71.    procedure Set_Image_Decimal
  72.      (V     : Integer;
  73.       S     : out String;
  74.       P     : in out Natural;
  75.       Scale : Integer;
  76.       Fore  : Natural;
  77.       Aft   : Natural;
  78.       Exp   : Natural)
  79.    is
  80.       Digs : String := Image_Integer (V);
  81.       --  Sign and digits of decimal value
  82.  
  83.    begin
  84.       Set_Decimal_Digits (Digs, Digs'Length, S, P, Scale, Fore, Aft, Exp);
  85.    end Set_Image_Decimal;
  86.  
  87.    ------------------------
  88.    -- Set_Decimal_Digits --
  89.    ------------------------
  90.  
  91.    procedure Set_Decimal_Digits
  92.      (Digs  : in out String;
  93.       NDigs : Natural;
  94.       S     : out String;
  95.       P     : in out Natural;
  96.       Scale : Integer;
  97.       Fore  : Natural;
  98.       Aft   : Natural;
  99.       Exp   : Natural)
  100.    is
  101.       Minus : constant Boolean := (Digs (1) = '-');
  102.       --  Set True if input is negative
  103.  
  104.       Zero : Boolean := (Digs (2) = '0');
  105.       --  Set True if input is exactly zero (only case when a leading zero
  106.       --  is permitted in the input string given to this procedure). This
  107.       --  flag can get set later if rounding causes the value to become zero.
  108.  
  109.       FD : Natural := 2;
  110.       --  First digit position of digits remaining to be processed
  111.  
  112.       LD : Natural := NDigs;
  113.       --  Last digit position of digits remaining to be processed
  114.  
  115.       ND : Natural := NDigs - 1;
  116.       --  Number of digits remaining to be processed (LD - FD + 1)
  117.  
  118.       Digits_Before_Point : Integer := ND - Scale;
  119.       --  Number of digits before decimal point in the input value. This
  120.       --  value can be negative if the input value is less than 0.1, so
  121.       --  it is an indication of the current exponent. Digits_Before_Point
  122.       --  is adjusted if the rounding step generates an extra digit.
  123.  
  124.       Digits_After_Point : constant Natural := Integer'Max (1, Aft);
  125.       --  Digit positions after decimal point in result string
  126.  
  127.       Expon : Integer;
  128.       --  Integer value of exponent
  129.  
  130.       procedure Round (N : Natural);
  131.       --  Round the number in Digs. N is the position of the last digit to be
  132.       --  retained in the rounded position (rounding is based on Digs (N + 1)
  133.       --  FD, LD, ND are reset as necessary if required. Note that if the
  134.       --  result value rounds up (e.g. 9.99 => 10.0), an extra digit can be
  135.       --  placed in the sign position as a result of the rounding, this is
  136.       --  the case in which FD is adjusted.
  137.  
  138.       procedure Set (C : Character);
  139.       pragma Inline (Set);
  140.       --  Sets character C in output buffer
  141.  
  142.       procedure Set_Blanks_And_Sign (N : Integer);
  143.       --  Sets leading blanks and minus sign if needed. N is the number of
  144.       --  positions to be filled (a minus sign is output even if N is zero
  145.       --  or negative, For a positive value, if N is non-positive, then
  146.       --  a leading blank is filled.
  147.  
  148.       procedure Set_Digits (S, E : Natural);
  149.       pragma Inline (Set_Digits);
  150.       --  Set digits S through E from Digs, no effect if S > E
  151.  
  152.       procedure Set_Zeroes (N : Integer);
  153.       pragma Inline (Set_Zeroes);
  154.       --  Set N zeroes, no effect if N is negative
  155.  
  156.       procedure Round (N : Natural) is
  157.          D : Character;
  158.  
  159.       begin
  160.          --  Nothing to do if rounding at or past last digit
  161.  
  162.          if N >= LD then
  163.             return;
  164.  
  165.          --  Cases of rounding before the initial digit
  166.  
  167.          elsif N < FD then
  168.  
  169.             --  The result is zero, unless we are rounding just before
  170.             --  the first digit, and the first digit is five or more.
  171.  
  172.             if N = 1 and then Digs (2) >= '5' then
  173.                Digs (1) := '1';
  174.             else
  175.                Digs (1) := '0';
  176.                Zero := True;
  177.             end if;
  178.  
  179.             Digits_Before_Point := Digits_Before_Point + 1;
  180.             FD := 1;
  181.             LD := 1;
  182.             ND := 1;
  183.  
  184.          --  Normal case of rounding an existing digit
  185.  
  186.          else
  187.             LD := N;
  188.             ND := LD - 1;
  189.  
  190.             if Digs (N + 1) >= '5' then
  191.                for J in reverse 2 .. N loop
  192.                   D := Character'Succ (Digs (J));
  193.  
  194.                   if D <= '9' then
  195.                      Digs (J) := D;
  196.                      return;
  197.                   else
  198.                      Digs (J) := '0';
  199.                   end if;
  200.                end loop;
  201.  
  202.                --  Here the rounding overflows into the sign position. That's
  203.                --  OK, because we already captured the value of the sign and
  204.                --  we are in any case destroying the value in the Digs buffer
  205.  
  206.                Digs (1) := '1';
  207.                FD := 1;
  208.                ND := ND + 1;
  209.                Digits_Before_Point := Digits_Before_Point + 1;
  210.             end if;
  211.          end if;
  212.       end Round;
  213.  
  214.       procedure Set (C : Character) is
  215.       begin
  216.          P := P + 1;
  217.          S (P) := C;
  218.       end Set;
  219.  
  220.       procedure Set_Blanks_And_Sign (N : Integer) is
  221.          W : Integer := N;
  222.  
  223.       begin
  224.          if Minus then
  225.             W := W - 1;
  226.  
  227.             for J in 1 .. W loop
  228.                Set (' ');
  229.             end loop;
  230.  
  231.             Set ('-');
  232.  
  233.          else
  234.             for J in 1 .. W loop
  235.                Set (' ');
  236.             end loop;
  237.          end if;
  238.       end Set_Blanks_And_Sign;
  239.  
  240.       procedure Set_Digits (S, E : Natural) is
  241.       begin
  242.          for J in S .. E loop
  243.             Set (Digs (J));
  244.          end loop;
  245.       end Set_Digits;
  246.  
  247.       procedure Set_Zeroes (N : Integer) is
  248.       begin
  249.          for J in 1 .. N loop
  250.             Set ('0');
  251.          end loop;
  252.       end Set_Zeroes;
  253.  
  254.    --  Start of processing for Set_Decimal_Digits
  255.  
  256.    begin
  257.       --  Case of exponent given
  258.  
  259.       if Exp > 0 then
  260.          Set_Blanks_And_Sign (Fore - 1);
  261.          Round (Aft + 2);
  262.          Set (Digs (FD));
  263.          FD := FD + 1;
  264.          ND := ND - 1;
  265.          Set ('.');
  266.  
  267.          if ND >= Digits_After_Point then
  268.             Set_Digits (FD, FD + Digits_After_Point - 1);
  269.  
  270.          else
  271.             Set_Digits (FD, LD);
  272.             Set_Zeroes (Digits_After_Point - ND);
  273.          end if;
  274.  
  275.          --  Calculate exponent. The number of digits before the decimal point
  276.          --  in the input is Digits_Before_Point, and the number of digits
  277.          --  before the decimal point in the output is 1, so we can get the
  278.          --  exponent as the difference between these two values. The one
  279.          --  exception is for the value zero, which by convention has an
  280.          --  exponent of +0.
  281.  
  282.          if Zero then
  283.             Expon := 0;
  284.          else
  285.             Expon := Digits_Before_Point - 1;
  286.          end if;
  287.  
  288.          Set ('E');
  289.          ND := 0;
  290.  
  291.          if Expon >= 0 then
  292.             Set ('+');
  293.             Set_Image_Integer (Expon, Digs, ND);
  294.          else
  295.             Set ('-');
  296.             Set_Image_Integer (-Expon, Digs, ND);
  297.          end if;
  298.  
  299.          Set_Zeroes (Exp - ND - 1);
  300.          Set_Digits (1, ND);
  301.          return;
  302.  
  303.       --  Case of no exponent given. To make these cases clear, we use
  304.       --  examples. For all the examples, we assume Fore = 2, Aft = 3.
  305.       --  A P in the example input string is an implied zero position,
  306.       --  not included in the input string.
  307.  
  308.       else
  309.          --  Round at correct position
  310.          --    Input: 4PP      => unchanged
  311.          --    Input: 400.03   => unchanged
  312.          --    Input  3.4567   => 3.457
  313.          --    Input: 9.9999   => 10.000
  314.          --    Input: 0.PPP5   => 0.001
  315.          --    Input: 0.PPP4   => 0
  316.          --    Input: 0.00003  => 0
  317.  
  318.          Round (LD - (Scale - Digits_After_Point));
  319.  
  320.          --  No digits before point in input
  321.          --    Input: .123   Output: 0.123
  322.          --    Input: .PP3   Output: 0.003
  323.  
  324.          if Digits_Before_Point <= 0 then
  325.             Set_Blanks_And_Sign (Fore - 1);
  326.             Set ('0');
  327.             Set ('.');
  328.  
  329.             Set_Zeroes (Digits_After_Point - ND);
  330.             Set_Digits (FD, LD);
  331.  
  332.          --  At least one digit before point in input
  333.  
  334.          else
  335.             Set_Blanks_And_Sign (Fore - Digits_Before_Point);
  336.  
  337.             --  Less digits in input than are needed before point
  338.             --    Input: 1PP  Output: 100.000
  339.  
  340.             if ND < Digits_Before_Point then
  341.                Set_Digits (FD, LD);
  342.                Set_Zeroes (Digits_Before_Point - ND);
  343.                Set ('.');
  344.                Set_Zeroes (Digits_After_Point);
  345.  
  346.             --  Input has full amount of digits before decimal point
  347.  
  348.             else
  349.                Set_Digits (FD, FD + Digits_Before_Point - 1);
  350.                Set ('.');
  351.                Set_Digits (FD + Digits_Before_Point, LD);
  352.                Set_Zeroes (Digits_After_Point - (ND - Digits_Before_Point));
  353.             end if;
  354.          end if;
  355.       end if;
  356.  
  357.    end Set_Decimal_Digits;
  358.  
  359. end System.Img_Dec;
  360.