[Ada] Correctly round Value attribute for floating point in more cases

This provides correct rounding in the IEEE 754 sense for the Value attribute
of floating-point types in more cases, by switching from tables of powers of
10 to tables of powers of 5 for precomputed values, thus making it possible
to use a single divide for denormals and normalized numbers just above them.

Although this significantly increases the size of the tables, object files
for them are still quite small (1, 2 and 4 KB respectively on x86-64).

gcc/ada/

	* libgnat/s-powflt.ads (Powfive): New constant array.
	* libgnat/s-powlfl.ads (Powfive): Likewise.
	(Powfive_100): New constant.
	(Powfive_200): Likewise.
	(Powfive_300): Likewise.
	* libgnat/s-powllf.ads (Powfive): New constant array.
	(Powfive_100): New constant.
	(Powfive_200): Likewise.
	(Powfive_300): Likewise.
	* libgnat/s-valflt.ads (Impl): Replace Powten with Powfive and pass
	Null_Address for the address of large constants.
	* libgnat/s-vallfl.ads (Impl): Replace Powten with Powfive and pass
	the address of large constants.
	* libgnat/s-valllf.ads (Impl): Likewise.
	* libgnat/s-valrea.ads (System.Val_Real): Replace Powten_Address
	with Powfive_Address and add Powfive_{1,2,3}00_Address parameters.
	* libgnat/s-valrea.adb (Is_Large_Type): New boolean constant.
	(Is_Very_Large_Type): Likewise.
	(Maxexp32): Change value of 10 to that of 5.
	(Maxexp64): Likewise.
	(Maxexp80): Likewise.
	(Integer_to_Real): Use a combination of tables of powers of 5 and
	scaling if the base is 10.
	(Large_Powten): Rename into...
	(Large_Powfive): ...this.  Add support for large constants.
	(Large_Powfive): New overloaded function for very large exponents.
This commit is contained in:
Eric Botcazou 2022-08-05 09:42:58 +02:00 committed by Marc Poulhiès
parent 8b9bbdc362
commit e9bac0faa1
8 changed files with 305 additions and 55 deletions

View File

@ -29,17 +29,41 @@
-- --
------------------------------------------------------------------------------
-- This package provides a powers of ten table used for real conversions
-- This package provides tables of powers used for real conversions
package System.Powten_Flt is
pragma Pure;
Maxpow_Exact : constant := 10;
-- Largest power of ten exactly representable with Float. It is equal to
-- Largest power of five exactly representable with Float. It is equal to
-- floor (M * log 2 / log 5), when M is the size of the mantissa (24).
-- It also works for any number of the form 5*(2**N) and in particular 10.
Maxpow : constant := Maxpow_Exact * 2;
-- Largest power of ten exactly representable with a double Float
-- Largest power of five exactly representable with double Float
Powfive : constant array (0 .. Maxpow, 1 .. 2) of Float :=
[00 => [5.0**00, 0.0],
01 => [5.0**01, 0.0],
02 => [5.0**02, 0.0],
03 => [5.0**03, 0.0],
04 => [5.0**04, 0.0],
05 => [5.0**05, 0.0],
06 => [5.0**06, 0.0],
07 => [5.0**07, 0.0],
08 => [5.0**08, 0.0],
09 => [5.0**09, 0.0],
10 => [5.0**10, 0.0],
11 => [5.0**11, 5.0**11 - Float'Machine (5.0**11)],
12 => [5.0**12, 5.0**12 - Float'Machine (5.0**12)],
13 => [5.0**13, 5.0**13 - Float'Machine (5.0**13)],
14 => [5.0**14, 5.0**14 - Float'Machine (5.0**14)],
15 => [5.0**15, 5.0**15 - Float'Machine (5.0**15)],
16 => [5.0**16, 5.0**16 - Float'Machine (5.0**16)],
17 => [5.0**17, 5.0**17 - Float'Machine (5.0**17)],
18 => [5.0**18, 5.0**18 - Float'Machine (5.0**18)],
19 => [5.0**19, 5.0**19 - Float'Machine (5.0**19)],
20 => [5.0**20, 5.0**20 - Float'Machine (5.0**20)]];
Powten : constant array (0 .. Maxpow, 1 .. 2) of Float :=
[00 => [1.0E+00, 0.0],

View File

@ -29,17 +29,74 @@
-- --
------------------------------------------------------------------------------
-- This package provides a powers of ten table used for real conversions
-- This package provides tables of powers used for real conversions
package System.Powten_LFlt is
pragma Pure;
Maxpow_Exact : constant := 22;
-- Largest power of ten exactly representable with Long_Float. It is equal
-- Largest power of five exactly representable with Long_Float. It is equal
-- to floor (M * log 2 / log 5), when M is the size of the mantissa (53).
-- It also works for any number of the form 5*(2**N) and in particular 10.
Maxpow : constant := Maxpow_Exact * 2;
-- Largest power of ten exactly representable with a double Long_Float
-- Largest power of five exactly representable with double Long_Float
Powfive : constant array (0 .. Maxpow, 1 .. 2) of Long_Float :=
[00 => [5.0**00, 0.0],
01 => [5.0**01, 0.0],
02 => [5.0**02, 0.0],
03 => [5.0**03, 0.0],
04 => [5.0**04, 0.0],
05 => [5.0**05, 0.0],
06 => [5.0**06, 0.0],
07 => [5.0**07, 0.0],
08 => [5.0**08, 0.0],
09 => [5.0**09, 0.0],
10 => [5.0**10, 0.0],
11 => [5.0**11, 0.0],
12 => [5.0**12, 0.0],
13 => [5.0**13, 0.0],
14 => [5.0**14, 0.0],
15 => [5.0**15, 0.0],
16 => [5.0**16, 0.0],
17 => [5.0**17, 0.0],
18 => [5.0**18, 0.0],
19 => [5.0**19, 0.0],
20 => [5.0**20, 0.0],
21 => [5.0**21, 0.0],
22 => [5.0**22, 0.0],
23 => [5.0**23, 5.0**23 - Long_Float'Machine (5.0**23)],
24 => [5.0**24, 5.0**24 - Long_Float'Machine (5.0**24)],
25 => [5.0**25, 5.0**25 - Long_Float'Machine (5.0**25)],
26 => [5.0**26, 5.0**26 - Long_Float'Machine (5.0**26)],
27 => [5.0**27, 5.0**27 - Long_Float'Machine (5.0**27)],
28 => [5.0**28, 5.0**28 - Long_Float'Machine (5.0**28)],
29 => [5.0**29, 5.0**29 - Long_Float'Machine (5.0**29)],
30 => [5.0**30, 5.0**30 - Long_Float'Machine (5.0**30)],
31 => [5.0**31, 5.0**31 - Long_Float'Machine (5.0**31)],
32 => [5.0**32, 5.0**32 - Long_Float'Machine (5.0**32)],
33 => [5.0**33, 5.0**33 - Long_Float'Machine (5.0**33)],
34 => [5.0**34, 5.0**34 - Long_Float'Machine (5.0**34)],
35 => [5.0**35, 5.0**35 - Long_Float'Machine (5.0**35)],
36 => [5.0**36, 5.0**36 - Long_Float'Machine (5.0**36)],
37 => [5.0**37, 5.0**37 - Long_Float'Machine (5.0**37)],
38 => [5.0**38, 5.0**38 - Long_Float'Machine (5.0**38)],
39 => [5.0**39, 5.0**39 - Long_Float'Machine (5.0**39)],
40 => [5.0**40, 5.0**40 - Long_Float'Machine (5.0**40)],
41 => [5.0**41, 5.0**41 - Long_Float'Machine (5.0**41)],
42 => [5.0**42, 5.0**42 - Long_Float'Machine (5.0**42)],
43 => [5.0**43, 5.0**43 - Long_Float'Machine (5.0**43)],
44 => [5.0**44, 5.0**44 - Long_Float'Machine (5.0**44)]];
Powfive_100 : constant array (1 .. 2) of Long_Float :=
[5.0**100, 5.0**100 - Long_Float'Machine (5.0**100)];
Powfive_200 : constant array (1 .. 2) of Long_Float :=
[5.0**200, 5.0**200 - Long_Float'Machine (5.0**200)];
Powfive_300 : constant array (1 .. 2) of Long_Float :=
[5.0**300, 5.0**300 - Long_Float'Machine (5.0**300)];
Powten : constant array (0 .. Maxpow, 1 .. 2) of Long_Float :=
[00 => [1.0E+00, 0.0],

View File

@ -29,19 +29,86 @@
-- --
------------------------------------------------------------------------------
-- This package provides a powers of ten table used for real conversions
-- This package provides tables of powers used for real conversions
package System.Powten_LLF is
pragma Pure;
Maxpow_Exact : constant :=
(if Long_Long_Float'Machine_Mantissa = 64 then 27 else 22);
-- Largest power of ten exactly representable with Long_Long_Float. It is
-- Largest power of five exactly representable with Long_Long_Float. It is
-- equal to floor (M * log 2 / log 5), when M is the size of the mantissa
-- assumed to be either 64 for IEEE Extended or 53 for IEEE Double.
-- It also works for any number of the form 5*(2**N) and in particular 10.
Maxpow : constant := Maxpow_Exact * 2;
-- Largest power of ten exactly representable with a double Long_Long_Float
-- Largest power of five exactly representable with double Long_Long_Float
Powfive : constant array (0 .. 54, 1 .. 2) of Long_Long_Float :=
[00 => [5.0**00, 0.0],
01 => [5.0**01, 0.0],
02 => [5.0**02, 0.0],
03 => [5.0**03, 0.0],
04 => [5.0**04, 0.0],
05 => [5.0**05, 0.0],
06 => [5.0**06, 0.0],
07 => [5.0**07, 0.0],
08 => [5.0**08, 0.0],
09 => [5.0**09, 0.0],
10 => [5.0**10, 0.0],
11 => [5.0**11, 0.0],
12 => [5.0**12, 0.0],
13 => [5.0**13, 0.0],
14 => [5.0**14, 0.0],
15 => [5.0**15, 0.0],
16 => [5.0**16, 0.0],
17 => [5.0**17, 0.0],
18 => [5.0**18, 0.0],
19 => [5.0**19, 0.0],
20 => [5.0**20, 0.0],
21 => [5.0**21, 0.0],
22 => [5.0**22, 0.0],
23 => [5.0**23, 5.0**23 - Long_Long_Float'Machine (5.0**23)],
24 => [5.0**24, 5.0**24 - Long_Long_Float'Machine (5.0**24)],
25 => [5.0**25, 5.0**25 - Long_Long_Float'Machine (5.0**25)],
26 => [5.0**26, 5.0**26 - Long_Long_Float'Machine (5.0**26)],
27 => [5.0**27, 5.0**27 - Long_Long_Float'Machine (5.0**27)],
28 => [5.0**28, 5.0**28 - Long_Long_Float'Machine (5.0**28)],
29 => [5.0**29, 5.0**29 - Long_Long_Float'Machine (5.0**29)],
30 => [5.0**30, 5.0**30 - Long_Long_Float'Machine (5.0**30)],
31 => [5.0**31, 5.0**31 - Long_Long_Float'Machine (5.0**31)],
32 => [5.0**32, 5.0**32 - Long_Long_Float'Machine (5.0**32)],
33 => [5.0**33, 5.0**33 - Long_Long_Float'Machine (5.0**33)],
34 => [5.0**34, 5.0**34 - Long_Long_Float'Machine (5.0**34)],
35 => [5.0**35, 5.0**35 - Long_Long_Float'Machine (5.0**35)],
36 => [5.0**36, 5.0**36 - Long_Long_Float'Machine (5.0**36)],
37 => [5.0**37, 5.0**37 - Long_Long_Float'Machine (5.0**37)],
38 => [5.0**38, 5.0**38 - Long_Long_Float'Machine (5.0**38)],
39 => [5.0**39, 5.0**39 - Long_Long_Float'Machine (5.0**39)],
40 => [5.0**40, 5.0**40 - Long_Long_Float'Machine (5.0**40)],
41 => [5.0**41, 5.0**41 - Long_Long_Float'Machine (5.0**41)],
42 => [5.0**42, 5.0**42 - Long_Long_Float'Machine (5.0**42)],
43 => [5.0**43, 5.0**43 - Long_Long_Float'Machine (5.0**43)],
44 => [5.0**44, 5.0**44 - Long_Long_Float'Machine (5.0**44)],
45 => [5.0**45, 5.0**45 - Long_Long_Float'Machine (5.0**45)],
46 => [5.0**46, 5.0**46 - Long_Long_Float'Machine (5.0**46)],
47 => [5.0**47, 5.0**47 - Long_Long_Float'Machine (5.0**47)],
48 => [5.0**48, 5.0**48 - Long_Long_Float'Machine (5.0**48)],
49 => [5.0**49, 5.0**49 - Long_Long_Float'Machine (5.0**49)],
50 => [5.0**50, 5.0**50 - Long_Long_Float'Machine (5.0**50)],
51 => [5.0**51, 5.0**51 - Long_Long_Float'Machine (5.0**51)],
52 => [5.0**52, 5.0**52 - Long_Long_Float'Machine (5.0**52)],
53 => [5.0**53, 5.0**53 - Long_Long_Float'Machine (5.0**53)],
54 => [5.0**54, 5.0**54 - Long_Long_Float'Machine (5.0**54)]];
Powfive_100 : constant array (1 .. 2) of Long_Long_Float :=
[5.0**100, 5.0**100 - Long_Long_Float'Machine (5.0**100)];
Powfive_200 : constant array (1 .. 2) of Long_Long_Float :=
[5.0**200, 5.0**200 - Long_Long_Float'Machine (5.0**200)];
Powfive_300 : constant array (1 .. 2) of Long_Long_Float :=
[5.0**300, 5.0**300 - Long_Long_Float'Machine (5.0**300)];
Powten : constant array (0 .. 54, 1 .. 2) of Long_Long_Float :=
[00 => [1.0E+00, 0.0],

View File

@ -42,7 +42,10 @@ package System.Val_Flt is
package Impl is new Val_Real
(Float,
System.Powten_Flt.Maxpow,
System.Powten_Flt.Powten'Address,
System.Powten_Flt.Powfive'Address,
System.Null_Address,
System.Null_Address,
System.Null_Address,
Unsigned_Types.Unsigned);
function Scan_Float

View File

@ -42,7 +42,10 @@ package System.Val_LFlt is
package Impl is new Val_Real
(Long_Float,
System.Powten_LFlt.Maxpow,
System.Powten_LFlt.Powten'Address,
System.Powten_LFlt.Powfive'Address,
System.Powten_LFlt.Powfive_100'Address,
System.Powten_LFlt.Powfive_200'Address,
System.Powten_LFlt.Powfive_300'Address,
Unsigned_Types.Long_Long_Unsigned);
function Scan_Long_Float

View File

@ -42,7 +42,10 @@ package System.Val_LLF is
package Impl is new Val_Real
(Long_Long_Float,
System.Powten_LLF.Maxpow,
System.Powten_LLF.Powten'Address,
System.Powten_LLF.Powfive'Address,
System.Powten_LLF.Powfive_100'Address,
System.Powten_LLF.Powfive_200'Address,
System.Powten_LLF.Powfive_300'Address,
System.Unsigned_Types.Long_Long_Unsigned);
function Scan_Long_Long_Float

View File

@ -43,7 +43,11 @@ package body System.Val_Real is
pragma Assert (Num'Machine_Mantissa <= Uns'Size);
-- We need an unsigned type large enough to represent the mantissa
Is_Large_Type : constant Boolean := Num'Machine_Mantissa >= 53;
-- True if the floating-point type is at least IEEE Double
Precision_Limit : constant Uns := 2**Num'Machine_Mantissa - 1;
-- See below for the rationale
package Impl is new Value_R (Uns, 2, Precision_Limit, Round => False);
@ -55,18 +59,21 @@ package body System.Val_Real is
Maxexp32 : constant array (Base_T) of Positive :=
[2 => 127, 3 => 80, 4 => 63, 5 => 55, 6 => 49,
7 => 45, 8 => 42, 9 => 40, 10 => 38, 11 => 37,
7 => 45, 8 => 42, 9 => 40, 10 => 55, 11 => 37,
12 => 35, 13 => 34, 14 => 33, 15 => 32, 16 => 31];
-- The actual value for 10 is 38 but we also use scaling for 10
Maxexp64 : constant array (Base_T) of Positive :=
[2 => 1023, 3 => 646, 4 => 511, 5 => 441, 6 => 396,
7 => 364, 8 => 341, 9 => 323, 10 => 308, 11 => 296,
7 => 364, 8 => 341, 9 => 323, 10 => 441, 11 => 296,
12 => 285, 13 => 276, 14 => 268, 15 => 262, 16 => 255];
-- The actual value for 10 is 308 but we also use scaling for 10
Maxexp80 : constant array (Base_T) of Positive :=
[2 => 16383, 3 => 10337, 4 => 8191, 5 => 7056, 6 => 6338,
7 => 5836, 8 => 5461, 9 => 5168, 10 => 4932, 11 => 4736,
7 => 5836, 8 => 5461, 9 => 5168, 10 => 7056, 11 => 4736,
12 => 4570, 13 => 4427, 14 => 4303, 15 => 4193, 16 => 4095];
-- The actual value for 10 is 4932 but we also use scaling for 10
package Double_Real is new System.Double_Real (Num);
use type Double_Real.Double_T;
@ -91,8 +98,11 @@ package body System.Val_Real is
Minus : Boolean) return Num;
-- Convert the real value from integer to real representation
function Large_Powten (Exp : Natural) return Double_T;
-- Return 10.0**Exp as a double number, where Exp > Maxpow
function Large_Powfive (Exp : Natural) return Double_T;
-- Return 5.0**Exp as a double number, where Exp > Maxpow
function Large_Powfive (Exp : Natural; S : out Natural) return Double_T;
-- Return Num'Scaling (5.0**Exp, -S) as a double number where Exp > Maxexp
---------------------
-- Integer_to_Real --
@ -177,13 +187,13 @@ package body System.Val_Real is
when 10 =>
declare
Powten : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powten);
for Powten'Address use Powten_Address;
Powfive : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powfive);
for Powfive'Address use Powfive_Address;
begin
if DS <= Maxpow then
D_Val := Powten (DS) * V1 + V2;
D_Val := Powfive (DS) * Num'Scaling (V1, DS) + V2;
S := Scale (2);
else
@ -224,43 +234,46 @@ package body System.Val_Real is
R_Val := Num'Scaling (Double_Real.To_Single (D_Val), S);
end;
-- If the base is 10, use a double implementation for the sake
-- of accuracy, to be removed when exponentiation is improved.
-- When the exponent is positive, we can do the computation
-- directly because, if the exponentiation overflows, then
-- the final value overflows as well. But when the exponent
-- is negative, we may need to do it in two steps to avoid
-- an artificial underflow.
-- If the base is 10, we use a double implementation for the sake
-- of accuracy combining powers of 5 and scaling attribute. Using
-- this combination is better than using powers of 10 only because
-- the Large_Powfive function may overflow only if the final value
-- will also either overflow or underflow, thus making it possible
-- to use a single division for the case of negative powers of 10.
when 10 =>
declare
Powten : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powten);
for Powten'Address use Powten_Address;
Powfive : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powfive);
for Powfive'Address use Powfive_Address;
RS : Natural;
begin
if S > 0 then
if S <= Maxpow then
D_Val := D_Val * Powten (S);
D_Val := D_Val * Powfive (S);
else
D_Val := D_Val * Large_Powten (S);
D_Val := D_Val * Large_Powfive (S);
end if;
else
if S < -Maxexp then
D_Val := D_Val / Large_Powten (Maxexp);
S := S + Maxexp;
end if;
if S >= -Maxpow then
D_Val := D_Val / Powten (-S);
D_Val := D_Val / Powfive (-S);
-- For small types, typically IEEE Single, the trick
-- described above does not fully work.
elsif not Is_Large_Type and then S < -Maxexp then
D_Val := D_Val / Large_Powfive (-S, RS);
S := S - RS;
else
D_Val := D_Val / Large_Powten (-S);
D_Val := D_Val / Large_Powfive (-S);
end if;
end if;
R_Val := Double_Real.To_Single (D_Val);
R_Val := Num'Scaling (Double_Real.To_Single (D_Val), S);
end;
-- Implementation for other bases with exponentiation
@ -302,14 +315,26 @@ package body System.Val_Real is
when Constraint_Error => Bad_Value (Str);
end Integer_to_Real;
------------------
-- Large_Powten --
------------------
-------------------
-- Large_Powfive --
-------------------
function Large_Powten (Exp : Natural) return Double_T is
Powten : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powten);
for Powten'Address use Powten_Address;
function Large_Powfive (Exp : Natural) return Double_T is
Powfive : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powfive);
for Powfive'Address use Powfive_Address;
Powfive_100 : constant Double_T;
pragma Import (Ada, Powfive_100);
for Powfive_100'Address use Powfive_100_Address;
Powfive_200 : constant Double_T;
pragma Import (Ada, Powfive_200);
for Powfive_200'Address use Powfive_200_Address;
Powfive_300 : constant Double_T;
pragma Import (Ada, Powfive_300);
for Powfive_300'Address use Powfive_300_Address;
R : Double_T;
E : Natural;
@ -317,18 +342,80 @@ package body System.Val_Real is
begin
pragma Assert (Exp > Maxpow);
R := Powten (Maxpow);
E := Exp - Maxpow;
if Is_Large_Type and then Exp >= 300 then
R := Powfive_300;
E := Exp - 300;
elsif Is_Large_Type and then Exp >= 200 then
R := Powfive_200;
E := Exp - 200;
elsif Is_Large_Type and then Exp >= 100 then
R := Powfive_100;
E := Exp - 100;
else
R := Powfive (Maxpow);
E := Exp - Maxpow;
end if;
while E > Maxpow loop
R := R * Powten (Maxpow);
R := R * Powfive (Maxpow);
E := E - Maxpow;
end loop;
R := R * Powten (E);
R := R * Powfive (E);
return R;
end Large_Powten;
end Large_Powfive;
function Large_Powfive (Exp : Natural; S : out Natural) return Double_T is
Maxexp : constant Positive :=
(if Num'Size = 32 then Maxexp32 (5)
elsif Num'Size = 64 then Maxexp64 (5)
elsif Num'Machine_Mantissa = 64 then Maxexp80 (5)
else raise Program_Error);
-- Maximum exponent of 5 that can fit in Num
Powfive : constant array (0 .. Maxpow) of Double_T;
pragma Import (Ada, Powfive);
for Powfive'Address use Powfive_Address;
R : Double_T;
E : Natural;
begin
pragma Assert (Exp > Maxexp);
pragma Warnings (Off, "-gnatw.a");
pragma Assert (not Is_Large_Type);
pragma Warnings (On, "-gnatw.a");
R := Powfive (Maxpow);
E := Exp - Maxpow;
-- If the exponent is not too large, then scale down the result so that
-- its final value does not overflow but, if it's too large, then do not
-- bother doing it since overflow is just fine. The scaling factor is -3
-- for every power of 5 above the maximum, in other words division by 8.
if Exp - Maxexp <= Maxpow then
S := 3 * (Exp - Maxexp);
R.Hi := Num'Scaling (R.Hi, -S);
R.Lo := Num'Scaling (R.Lo, -S);
else
S := 0;
end if;
while E > Maxpow loop
R := R * Powfive (Maxpow);
E := E - Maxpow;
end loop;
R := R * Powfive (E);
return R;
end Large_Powfive;
---------------
-- Scan_Real --

View File

@ -38,7 +38,13 @@ generic
Maxpow : Positive;
Powten_Address : System.Address;
Powfive_Address : System.Address;
Powfive_100_Address : System.Address;
Powfive_200_Address : System.Address;
Powfive_300_Address : System.Address;
type Uns is mod <>;