aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/libgnat/s-valrea.adb
blob: eb13faeea48c2b2703363741eb93a498c128cc6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                      S Y S T E M . V A L _ R E A L                       --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 1992-2023, Free Software Foundation, Inc.         --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with System.Double_Real;
with System.Float_Control;
with System.Unsigned_Types; use System.Unsigned_Types;
with System.Val_Util;       use System.Val_Util;
with System.Value_R;

pragma Warnings (Off, "non-static constant in preelaborated unit");
--  Every constant is static given our instantiation model

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);

   subtype Base_T is Unsigned range 2 .. 16;

   --  The following tables compute the maximum exponent of the base that can
   --  fit in the given floating-point format, that is to say the element at
   --  index N is the largest K such that N**K <= Num'Last.

   Maxexp32 : constant array (Base_T) of Positive :=
     [2  => 127, 3 => 80,  4 => 63,  5 => 55,  6 => 49,
      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 => 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 => 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;

   subtype Double_T is Double_Real.Double_T;
   --  The double floating-point type

   function Exact_Log2 (N : Unsigned) return Positive is
     (case N is
        when  2     => 1,
        when  4     => 2,
        when  8     => 3,
        when 16     => 4,
        when others => raise Program_Error);
   --  Return the exponent of a power of 2

   function Integer_to_Real
     (Str   : String;
      Val   : Impl.Value_Array;
      Base  : Unsigned;
      Scale : Impl.Scale_Array;
      Minus : Boolean) return Num;
   --  Convert the real value from integer to real representation

   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 --
   ---------------------

   function Integer_to_Real
     (Str   : String;
      Val   : Impl.Value_Array;
      Base  : Unsigned;
      Scale : Impl.Scale_Array;
      Minus : Boolean) return Num
   is
      pragma Assert (Base in 2 .. 16);

      pragma Assert (Num'Machine_Radix = 2);

      pragma Unsuppress (Range_Check);

      Maxexp : constant Positive :=
                 (if    Num'Size = 32             then Maxexp32 (Base)
                  elsif Num'Size = 64             then Maxexp64 (Base)
                  elsif Num'Machine_Mantissa = 64 then Maxexp80 (Base)
                  else  raise Program_Error);
      --  Maximum exponent of the base that can fit in Num

      D_Val : Double_T;
      R_Val : Num;
      S     : Integer;

   begin
      --  We call the floating-point processor reset routine so we can be sure
      --  that the x87 FPU is properly set for conversions. This is especially
      --  needed on Windows, where calls to the operating system randomly reset
      --  the processor into 64-bit mode.

      if Num'Machine_Mantissa = 64 then
         System.Float_Control.Reset;
      end if;

      --  First convert the integer mantissa into a double real. The conversion
      --  of each part is exact, given the precision limit we used above. Then,
      --  if the contribution of the low part might be nonnull, scale the high
      --  part appropriately and add the low part to the result.

      if Val (2) = 0 then
         D_Val := Double_Real.To_Double (Num (Val (1)));
         S := Scale (1);

      else
         declare
            V1 : constant Num := Num (Val (1));
            V2 : constant Num := Num (Val (2));

            DS : Positive;

         begin
            DS := Scale (1) - Scale (2);

            case Base is
               --  If the base is a power of two, we use the efficient Scaling
               --  attribute up to an amount worth a double mantissa.

               when 2 | 4 | 8 | 16 =>
                  declare
                     L : constant Positive := Exact_Log2 (Base);

                  begin
                     if DS <= 2 * Num'Machine_Mantissa / L then
                        DS := DS * L;
                        D_Val :=
                          Double_Real.Quick_Two_Sum (Num'Scaling (V1, DS), V2);
                        S := Scale (2);

                     else
                        D_Val := Double_Real.To_Double (V1);
                        S := Scale (1);
                     end if;
                  end;

               --  If the base is 10, we also scale up to an amount worth a
               --  double mantissa.

               when 10 =>
                  declare
                     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 := Powfive (DS) * Num'Scaling (V1, DS) + V2;
                        S := Scale (2);

                     else
                        D_Val := Double_Real.To_Double (V1);
                        S := Scale (1);
                     end if;
                  end;

               --  Inaccurate implementation for other bases

               when others =>
                  D_Val := Double_Real.To_Double (V1);
                  S := Scale (1);
            end case;
         end;
      end if;

      --  Compute the final value by applying the scaling, if any

      if (Val (1) = 0 and then Val (2) = 0) or else S = 0 then
         R_Val := Double_Real.To_Single (D_Val);

      else
         case Base is
            --  If the base is a power of two, we use the efficient Scaling
            --  attribute with an overflow check, if it is not 2, to catch
            --  ludicrous exponents that would result in an infinity or zero.

            when 2 | 4 | 8 | 16 =>
               declare
                  L : constant Positive := Exact_Log2 (Base);

               begin
                  if Integer'First / L <= S and then S <= Integer'Last / L then
                     S := S * L;
                  end if;

                  R_Val := Num'Scaling (Double_Real.To_Single (D_Val), S);
               end;

            --  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
                  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 * Powfive (S);
                     else
                        D_Val := D_Val * Large_Powfive (S);
                     end if;

                  else
                     if S >= -Maxpow then
                        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_Powfive (-S);
                     end if;
                  end if;

                  R_Val := Num'Scaling (Double_Real.To_Single (D_Val), S);
               end;

            --  Implementation for other bases with exponentiation

            --  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.

            when others =>
               declare
                  B : constant Num := Num (Base);

               begin
                  R_Val := Double_Real.To_Single (D_Val);

                  if S > 0 then
                     R_Val := R_Val * B ** S;

                  else
                     if S < -Maxexp then
                        R_Val := R_Val / B ** Maxexp;
                        S := S + Maxexp;
                     end if;

                     R_Val := R_Val / B ** (-S);
                  end if;
               end;
         end case;
      end if;

      --  Finally deal with initial minus sign, note that this processing is
      --  done even if Uval is zero, so that -0.0 is correctly interpreted.

      return (if Minus then -R_Val else R_Val);

   exception
      when Constraint_Error => Bad_Value (Str);
   end Integer_to_Real;

   -------------------
   -- Large_Powfive --
   -------------------

   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;

   begin
      pragma Assert (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 * 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 * Powfive (Maxpow);
         E := E - Maxpow;
      end loop;

      R := R * Powfive (E);

      return R;
   end Large_Powfive;

   ---------------
   -- Scan_Real --
   ---------------

   function Scan_Real
     (Str : String;
      Ptr : not null access Integer;
      Max : Integer) return Num
   is
      Base  : Unsigned;
      Scale : Impl.Scale_Array;
      Extra : Unsigned;
      Minus : Boolean;
      Val   : Impl.Value_Array;

   begin
      Val := Impl.Scan_Raw_Real (Str, Ptr, Max, Base, Scale, Extra, Minus);

      return Integer_to_Real (Str, Val, Base, Scale, Minus);
   end Scan_Real;

   ----------------
   -- Value_Real --
   ----------------

   function Value_Real (Str : String) return Num is
      Base  : Unsigned;
      Scale : Impl.Scale_Array;
      Extra : Unsigned;
      Minus : Boolean;
      Val   : Impl.Value_Array;

   begin
      Val := Impl.Value_Raw_Real (Str, Base, Scale, Extra, Minus);

      return Integer_to_Real (Str, Val, Base, Scale, Minus);
   end Value_Real;

end System.Val_Real;