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

Message ID 20220906071541.GA1280201@poulhies-Precision-5550
State New, archived
Headers
Series [Ada] Correctly round Value attribute for floating point in more cases |

Commit Message

Marc Poulhiès Sept. 6, 2022, 7:15 a.m. UTC
  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).

Tested on x86_64-pc-linux-gnu, committed on trunk

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.
  

Patch

diff --git a/gcc/ada/libgnat/s-powflt.ads b/gcc/ada/libgnat/s-powflt.ads
--- a/gcc/ada/libgnat/s-powflt.ads
+++ b/gcc/ada/libgnat/s-powflt.ads
@@ -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],


diff --git a/gcc/ada/libgnat/s-powlfl.ads b/gcc/ada/libgnat/s-powlfl.ads
--- a/gcc/ada/libgnat/s-powlfl.ads
+++ b/gcc/ada/libgnat/s-powlfl.ads
@@ -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],


diff --git a/gcc/ada/libgnat/s-powllf.ads b/gcc/ada/libgnat/s-powllf.ads
--- a/gcc/ada/libgnat/s-powllf.ads
+++ b/gcc/ada/libgnat/s-powllf.ads
@@ -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],


diff --git a/gcc/ada/libgnat/s-valflt.ads b/gcc/ada/libgnat/s-valflt.ads
--- a/gcc/ada/libgnat/s-valflt.ads
+++ b/gcc/ada/libgnat/s-valflt.ads
@@ -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


diff --git a/gcc/ada/libgnat/s-vallfl.ads b/gcc/ada/libgnat/s-vallfl.ads
--- a/gcc/ada/libgnat/s-vallfl.ads
+++ b/gcc/ada/libgnat/s-vallfl.ads
@@ -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


diff --git a/gcc/ada/libgnat/s-valllf.ads b/gcc/ada/libgnat/s-valllf.ads
--- a/gcc/ada/libgnat/s-valllf.ads
+++ b/gcc/ada/libgnat/s-valllf.ads
@@ -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


diff --git a/gcc/ada/libgnat/s-valrea.adb b/gcc/ada/libgnat/s-valrea.adb
--- a/gcc/ada/libgnat/s-valrea.adb
+++ b/gcc/ada/libgnat/s-valrea.adb
@@ -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);
+      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 * Powfive (Maxpow);
+         E := E - Maxpow;
+      end loop;
+
+      R := R * Powfive (E);
+
+      return R;
+   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 * 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;
 
    ---------------
    -- Scan_Real --


diff --git a/gcc/ada/libgnat/s-valrea.ads b/gcc/ada/libgnat/s-valrea.ads
--- a/gcc/ada/libgnat/s-valrea.ads
+++ b/gcc/ada/libgnat/s-valrea.ads
@@ -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 <>;