home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
gnat-2.06-src.tgz
/
tar.out
/
fsf
/
gnat
/
ada
/
s-imgrea.adb
< prev
next >
Wrap
Text File
|
1996-09-28
|
15KB
|
485 lines
------------------------------------------------------------------------------
-- --
-- GNAT RUNTIME COMPONENTS --
-- --
-- S Y S T E M . I M G _ R E A L --
-- --
-- B o d y --
-- --
-- $Revision: 1.26 $ --
-- --
-- Copyright (c) 1992,1993,1994,1995 NYU, All Rights Reserved --
-- --
-- The GNAT library is free software; you can redistribute it and/or modify --
-- it under terms of the GNU Library General Public License as published by --
-- the Free Software Foundation; either version 2, or (at your option) any --
-- later version. The GNAT library is distributed in the hope that it will --
-- be useful, but WITHOUT ANY WARRANTY; without even the implied warranty --
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU --
-- Library General Public License for more details. You should have --
-- received a copy of the GNU Library General Public License along with --
-- the GNAT library; see the file COPYING.LIB. If not, write to the Free --
-- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --
-- --
------------------------------------------------------------------------------
with System.Img_LLU; use System.Img_LLU;
with System.Img_Uns; use System.Img_Uns;
with System.Parameters;
with System.Powten_Table; use System.Powten_Table;
with System.Unsigned_Types; use System.Unsigned_Types;
package body System.Img_Real is
Maxdigs : constant := Long_Long_Unsigned'Width - 2;
-- Maximum decimal digits for type Long_Long_Unsigned. We assume that this
-- is large enough for the most accurate floating-point type around, which
-- is probably correct for pretty much all machines we are likely to see.
-- At worst, if this assumption is false, then we just loose some precision
-- for high accuracy floating-point, and that's OK, since we only promise
-- support of the numerics annex accuracy for IEEE machines anyway (and so
-- far there is no IEEE machine that would violate this assumption.
--
-- The -2 comes from 1 for the sign, and one for the extra digit, since
-- we need the maximum number of 9's that can be supported, e.g. for the
-- normal 64 bit case, Long_Long_Integer'Width is 21, since the maximum
-- value (approx 1.6 * 10**19) has 20 digits.
Unsdigs : constant := Unsigned'Width - 2;
-- Number of digits that can be converted using type Unsigned
-- See above for the explanation of the -2.
--------------------------------
-- Image_Ordinary_Fixed_Point --
--------------------------------
function Image_Ordinary_Fixed_Point
(V : Long_Long_Float;
Aft : Natural)
return String
is
P : Natural := 0;
S : String (1 .. Long_Long_Float'Width);
begin
Set_Image_Real (V, S, P, 2, Aft, 0);
return S (1 .. P);
end Image_Ordinary_Fixed_Point;
--------------------------
-- Image_Floating_Point --
--------------------------
function Image_Floating_Point
(V : Long_Long_Float;
Digs : Natural)
return String
is
P : Natural := 0;
S : String (1 .. Long_Long_Float'Width);
begin
Set_Image_Real (V, S, P, 2, Digs - 1, 4);
return S (1 .. P);
end Image_Floating_Point;
--------------------
-- Set_Image_Real --
--------------------
procedure Set_Image_Real
(V : Long_Long_Float;
S : out String;
P : in out Natural;
Fore : Natural;
Aft : Natural;
Exp : Natural)
is
NFrac : constant Natural := Natural'Max (Aft, 1);
Sign : Character;
X : Long_Long_Float;
X1 : Long_Long_Float;
X2 : Long_Long_Float;
Scale : Integer;
Expon : Integer;
Digs : String (1 .. 2 * System.Parameters.Field_Max);
-- Array used to hold digits of converted integer value. This is a
-- large enough buffer to accomodate ludicrous values of Fore and Aft.
Ndigs : Natural;
-- Number of digits stored in Digs (and also subscript of last digit)
procedure Adjust_Scale (S : Natural);
-- Adjusts the value in X by multiplying or dividing by a power of
-- ten so that it is in the range 10**(S-1) <= X < 10**S. Includes
-- adding 0.5 to round the result, readjusting if the rounding causes
-- the result to wander out of the range. Scale is adjusted to reflect
-- the power of ten used to divide the result (i.e. one is added to
-- the scale value for each division by 10.0, or one is subtracted
-- for each multiplication by 10.0).
procedure Convert_Integer;
-- Takes the value in X, outputs integer digits into Digs. On return,
-- Ndigs is set to the number of digits stored. The digits are stored
-- in Digs (1 .. Ndigs),
procedure Set (C : Character);
-- Sets character C in output buffer
procedure Set_Blanks_And_Sign (N : Integer);
-- Sets leading blanks and minus sign if needed. N is the number of
-- positions to be filled (a minus sign is output even if N is zero
-- or negative, but for a positive value, if N is non-positive, then
-- the call has no effect).
procedure Set_Digs (S, E : Natural);
-- Set digits S through E from Digs buffer. No effect if S > E
procedure Set_Special_Fill (N : Natural);
-- After outputting +Inf, -Inf or NaN, this routine fills out the
-- rest of the field with * characters. The argument is the number
-- of characters output so far (either 3 or 4)
procedure Set_Zeros (N : Integer);
-- Set N zeros, no effect if N is negative
pragma Inline (Set);
pragma Inline (Set_Digs);
pragma Inline (Set_Zeros);
procedure Adjust_Scale (S : Natural) is
Lo : Natural;
Hi : Natural;
Mid : Natural;
XP : Long_Long_Float;
begin
-- Cases where scaling up is required
if X < Powten (S - 1) then
-- What we are looking for is a power of ten to multiply X by
-- so that the result lies within the required range.
loop
XP := X * Powten (40);
exit when XP >= Powten (S - 1);
X := XP;
Scale := Scale - 40;
end loop;
-- Here we know that we must mutiply by at least 10**1 and 10**40
-- takes us too far, so use a binary search to find the right one.
Lo := 1;
Hi := 40;
loop
Mid := (Lo + Hi) / 2;
XP := X * Powten (Mid);
if XP < Powten (S - 1) then
Lo := Mid + 1;
elsif XP >= Powten (S) then
Hi := Mid - 1;
else
X := XP;
Scale := Scale - Mid;
exit;
end if;
end loop;
-- Cases where scaling down is required
elsif X >= Powten (S) then
-- What we are looking for is a power of ten to divide X by
-- so that the result lies within the required range.
loop
XP := X / Powten (40);
exit when XP < Powten (S);
X := XP;
Scale := Scale + 40;
end loop;
-- Here we know that we must divide by at least 10**1 and 10**40
-- takes us too far, so use a binary search to find the right one.
Lo := 1;
Hi := 40;
loop
Mid := (Lo + Hi) / 2;
XP := X / Powten (Mid);
if XP < Powten (S - 1) then
Hi := Mid - 1;
elsif XP >= Powten (S) then
Lo := Mid + 1;
else
X := XP;
Scale := Scale + Mid;
exit;
end if;
end loop;
-- Here we are already scaled right
else
null;
end if;
-- Round, readjusting scale if needed. Note that if a readjustment
-- occurs, then it is never necessary to round again, because there
-- is no possibility of such a second rounding causing a change.
X := X + 0.5;
if X > Powten (S) then
X := X / 10.0;
Scale := Scale + 1;
end if;
end Adjust_Scale;
procedure Convert_Integer is
begin
-- Use Unsigned routine if possible, since on many machines it will
-- be significantly more efficient than the Long_Long_Unsigned one.
if X < Powten (Unsdigs) then
Ndigs := 0;
Set_Image_Unsigned
(Unsigned (Long_Long_Float'Truncation (X)),
Digs, Ndigs);
-- But if we want more digits than fit in Unsigned, we have to use
-- the Long_Long_Unsigned routine after all.
else
Ndigs := 0;
Set_Image_Long_Long_Unsigned
(Long_Long_Unsigned (Long_Long_Float'Truncation (X)),
Digs, Ndigs);
end if;
end Convert_Integer;
procedure Set (C : Character) is
begin
P := P + 1;
S (P) := C;
end Set;
procedure Set_Blanks_And_Sign (N : Integer) is
W : Integer := N;
begin
if Sign = '-' then
for J in 1 .. N - 1 loop
Set (' ');
end loop;
Set ('-');
else
for J in 1 .. N loop
Set (' ');
end loop;
end if;
end Set_Blanks_And_Sign;
procedure Set_Digs (S, E : Natural) is
begin
for J in S .. E loop
Set (Digs (J));
end loop;
end Set_Digs;
procedure Set_Special_Fill (N : Natural) is
F : Natural;
begin
F := Fore + 1 + Aft - N;
if Exp /= 0 then
F := F + Exp + 1;
end if;
for J in 1 .. F loop
Set ('*');
end loop;
end Set_Special_Fill;
procedure Set_Zeros (N : Integer) is
begin
for J in 1 .. N loop
Set ('0');
end loop;
end Set_Zeros;
-- Start of processing for Set_Image_Real
begin
Scale := 0;
Sign := '+';
-- Positive values
if V > 0.0 then
X := V;
-- Negative values
elsif V < 0.0 then
X := -V;
Sign := '-';
-- Zero values
elsif V = 0.0 then
Set_Blanks_And_Sign (Fore - 1);
Set ('0');
Set ('.');
Set_Zeros (NFrac);
if Exp /= 0 then
Set ('E');
Set ('+');
Set_Zeros (Natural'Max (1, Exp - 1));
end if;
return;
-- Only NaN's fail all three of the above tests!
else
Set ('N');
Set ('a');
Set ('N');
Set_Special_Fill (3);
return;
end if;
-- If value is greater than Long_Long_Float'Last it is infinite
if X > Long_Long_Float'Last then
Set (Sign);
Set ('I');
Set ('n');
Set ('f');
Set_Special_Fill (4);
-- Case of non-zero value with Exp = 0
elsif Exp = 0 then
-- Multiply by 10 ** NFrac to get an integer value to output and
-- then add 0.5 to round the result, except that if we are already
-- greater than 10**Maxdigs, or the multiplication would make us
-- larger than that, then we don't want to do the multiplication
-- after all.
if X < Powten (Maxdigs) then
X := X * Powten (NFrac);
X := X + 0.5;
end if;
-- If that makes us too large, it means that we have some digits
-- in the output that are non-significant, and will be output as
-- zeroes, so in this case we need to scale so that:
-- 10 ** (Maxdigs - 1) <= X < 10 ** Maxdigs
if X >= Powten (Maxdigs) then
Adjust_Scale (Maxdigs);
end if;
Convert_Integer;
-- If we had to scale, then we certainly scaled down, i.e. Scale is
-- the number of insignificant zero digits to be output at the end,
-- so add them to the resulting integer value.
for J in 1 .. Scale loop
Ndigs := Ndigs + 1;
Digs (Ndigs) := '0';
end loop;
-- If number of available digits is less or equal to NFrac,
-- then we need an extra zero before the decimal point.
if Ndigs <= NFrac then
Set_Blanks_And_Sign (Fore - 1);
Set ('0');
Set ('.');
Set_Zeros (NFrac - Ndigs);
Set_Digs (1, Ndigs);
-- Normal case with some digits before the decimal point
else
Set_Blanks_And_Sign (Fore - (Ndigs - NFrac));
Set_Digs (1, Ndigs - NFrac);
Set ('.');
Set_Digs (Ndigs - NFrac + 1, Ndigs);
end if;
-- Case of non-zero value with non-zero Exp value
else
-- If NFrac is less than Maxdigs, then all the fraction digits are
-- significant, so we can scale the resulting integer accordingly.
if NFrac < Maxdigs then
Adjust_Scale (NFrac + 1);
Convert_Integer;
-- Otherwise, we get the maximum number of digits available
else
Adjust_Scale (Maxdigs);
Convert_Integer;
for J in 1 .. NFrac - Maxdigs + 1 loop
Ndigs := Ndigs + 1;
Digs (Ndigs) := '0';
Scale := Scale - 1;
end loop;
end if;
Set_Blanks_And_Sign (Fore - 1);
Set (Digs (1));
Set ('.');
Set_Digs (2, Ndigs);
-- The exponent is the scaling factor adjusted for the digits
-- that we output after the decimal point, since these were
-- included in the scaled digits that we output.
Expon := Scale + NFrac;
Set ('E');
Ndigs := 0;
if Expon >= 0 then
Set ('+');
Set_Image_Unsigned (Unsigned (Expon), Digs, Ndigs);
else
Set ('-');
Set_Image_Unsigned (Unsigned (-Expon), Digs, Ndigs);
end if;
Set_Zeros (Exp - Ndigs - 1);
Set_Digs (1, Ndigs);
end if;
end Set_Image_Real;
end System.Img_Real;