]> gcc.gnu.org Git - gcc.git/blob - gcc/ada/libgnat/a-tifiio.adb
[Ada] Expand integer-only implementation of ordinary fixed-point types
[gcc.git] / gcc / ada / libgnat / a-tifiio.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME COMPONENTS --
4 -- --
5 -- A D A . T E X T _ I O . F I X E D _ I O --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1992-2020, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
17 -- --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
21 -- --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
26 -- --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
29 -- --
30 ------------------------------------------------------------------------------
31
32 -- Fixed point I/O
33 -- ---------------
34
35 -- The following text documents implementation details of the fixed point
36 -- input/output routines in the GNAT runtime. The first part describes the
37 -- general properties of fixed point types as defined by the Ada standard,
38 -- including the Information Systems Annex.
39
40 -- Subsequently these are reduced to implementation constraints and the impact
41 -- of these constraints on a few possible approaches to input/output is given.
42 -- Based on this analysis, a specific implementation is selected for use in
43 -- the GNAT runtime. Finally, the chosen algorithm is analyzed numerically in
44 -- order to provide user-level documentation on limits for range and precision
45 -- of fixed point types as well as accuracy of input/output conversions.
46
47 -- -------------------------------------------
48 -- - General Properties of Fixed Point Types -
49 -- -------------------------------------------
50
51 -- Operations on fixed point types, other than input/output, are not important
52 -- for the purpose of this document. Only the set of values that a fixed point
53 -- type can represent and the input/output operations are significant.
54
55 -- Values
56 -- ------
57
58 -- The set of values of a fixed point type comprise the integral multiples of
59 -- a number called the small of the type. The small can be either a power of
60 -- two, a power of ten or (if the implementation allows) an arbitrary strictly
61 -- positive real value.
62
63 -- Implementations need to support ordinary fixed point types with a precision
64 -- of at least 24 bits, and (in order to comply with the Information Systems
65 -- Annex) decimal fixed point types with at least 18 digits. For the rest, no
66 -- requirements exist for the minimal small and range that must be supported.
67
68 -- Operations
69 -- ----------
70
71 -- 'Image and 'Wide_Image (see RM 3.5(34))
72
73 -- These attributes return a decimal real literal best approximating
74 -- the value (rounded away from zero if halfway between) with a
75 -- single leading character that is either a minus sign or a space,
76 -- one or more digits before the decimal point (with no redundant
77 -- leading zeros), a decimal point, and N digits after the decimal
78 -- point. For a subtype S, the value of N is S'Aft, the smallest
79 -- positive integer such that (10**N)*S'Delta is greater or equal to
80 -- one, see RM 3.5.10(5).
81
82 -- For an arbitrary small, this means large number arithmetic needs
83 -- to be performed.
84
85 -- Put (see RM A.10.9(22-26))
86
87 -- The requirements for Put add no extra constraints over the image
88 -- attributes, although it would be nice to be able to output more
89 -- than S'Aft digits after the decimal point for values of subtype S.
90
91 -- 'Value and 'Wide_Value attribute (RM 3.5(40-55))
92
93 -- Since the input can be given in any base in the range 2..16,
94 -- accurate conversion to a fixed point number may require
95 -- arbitrary precision arithmetic if there is no limit on the
96 -- magnitude of the small of the fixed point type.
97
98 -- Get (see RM A.10.9(12-21))
99
100 -- The requirements for Get are identical to those of the Value
101 -- attribute.
102
103 -- ------------------------------
104 -- - Implementation Constraints -
105 -- ------------------------------
106
107 -- The requirements listed above for the input/output operations lead to
108 -- significant complexity, if no constraints are put on supported smalls.
109
110 -- Implementation Strategies
111 -- -------------------------
112
113 -- * Floating point arithmetic
114 -- * Arbitrary-precision integer arithmetic
115 -- * Fixed-precision integer arithmetic
116
117 -- Although it seems convenient to convert fixed point numbers to floating
118 -- point and then print them, this leads to a number of restrictions.
119 -- The first one is precision. The widest floating-point type generally
120 -- available has 53 bits of mantissa. This means that Fine_Delta cannot
121 -- be less than 2.0**(-53).
122
123 -- In GNAT, Fine_Delta is 2.0**(-63), and Duration for example is a 64-bit
124 -- type. This means that a floating-point type with 63 bits of mantissa needs
125 -- to be used, which is only generally available on the x86 architecture. It
126 -- would still be possible to use multi-precision floating point to perform
127 -- calculations using longer mantissas, but this is a much harder approach.
128
129 -- The base conversions needed for input/output of (non-decimal) fixed point
130 -- types can be seen as pairs of integer multiplications and divisions.
131
132 -- Arbitrary-precision integer arithmetic would be suitable for the job at
133 -- hand, but has the drawback that it is very heavy implementation-wise.
134 -- Especially in embedded systems, where fixed point types are often used,
135 -- it may not be desirable to require large amounts of storage and time
136 -- for fixed I/O operations.
137
138 -- Fixed-precision integer arithmetic has the advantage of simplicity and
139 -- speed. For the most common fixed point types this would be a perfect
140 -- solution. The downside however may be a too limited set of acceptable
141 -- fixed point types.
142
143 with Interfaces;
144 with Ada.Text_IO.Fixed_Aux;
145 with Ada.Text_IO.Float_Aux;
146 with System.Img_Fixed_32; use System.Img_Fixed_32;
147 with System.Img_Fixed_64; use System.Img_Fixed_64;
148 with System.Val_Fixed_32; use System.Val_Fixed_32;
149 with System.Val_Fixed_64; use System.Val_Fixed_64;
150
151 package body Ada.Text_IO.Fixed_IO is
152
153 -- Note: we still use the floating-point I/O routines for types whose small
154 -- is not the ratio of two sufficiently small integers. This will result in
155 -- inaccuracies for fixed point types that require more precision than is
156 -- available in Long_Long_Float.
157
158 subtype Int32 is Interfaces.Integer_32; use type Int32;
159 subtype Int64 is Interfaces.Integer_64; use type Int64;
160
161 package Aux32 is new
162 Ada.Text_IO.Fixed_Aux (Int32, Scan_Fixed32, Set_Image_Fixed32);
163
164 package Aux64 is new
165 Ada.Text_IO.Fixed_Aux (Int64, Scan_Fixed64, Set_Image_Fixed64);
166
167 -- Throughout this generic body, we distinguish between the case where type
168 -- Int32 is OK and where type Int64 is OK. These boolean constants are used
169 -- to test for this, such that only code for the relevant case is included
170 -- in the instance; that's why the computation of their value must be fully
171 -- static (although it is not a static expressions in the RM sense).
172
173 OK_Get_32 : constant Boolean :=
174 Num'Object_Size <= 32
175 and then
176 ((Num'Small_Numerator = 1 and then Num'Small_Denominator <= 2**31)
177 or else
178 (Num'Small_Denominator = 1 and then Num'Small_Numerator <= 2**31)
179 or else
180 (Num'Small_Numerator <= 2**27
181 and then Num'Small_Denominator <= 2**27));
182 -- These conditions are derived from the prerequisites of System.Value_F
183
184 OK_Put_32 : constant Boolean :=
185 Num'Object_Size <= 32
186 and then
187 ((Num'Small_Numerator = 1 and then Num'Small_Denominator <= 2**31)
188 or else
189 (Num'Small_Denominator = 1 and then Num'Small_Numerator <= 2**31)
190 or else
191 (Num'Small_Numerator < Num'Small_Denominator
192 and then Num'Small_Denominator <= 2**27)
193 or else
194 (Num'Small_Denominator < Num'Small_Numerator
195 and then Num'Small_Numerator <= 2**25));
196 -- These conditions are derived from the prerequisites of System.Image_F
197
198 OK_Get_64 : constant Boolean :=
199 Num'Object_Size <= 64
200 and then
201 ((Num'Small_Numerator = 1 and then Num'Small_Denominator <= 2**63)
202 or else
203 (Num'Small_Denominator = 1 and then Num'Small_Numerator <= 2**63)
204 or else
205 (Num'Small_Numerator <= 2**59
206 and then Num'Small_Denominator <= 2**59));
207 -- These conditions are derived from the prerequisites of System.Value_F
208
209 OK_Put_64 : constant Boolean :=
210 Num'Object_Size <= 64
211 and then
212 ((Num'Small_Numerator = 1 and then Num'Small_Denominator <= 2**63)
213 or else
214 (Num'Small_Denominator = 1 and then Num'Small_Numerator <= 2**63)
215 or else
216 (Num'Small_Numerator < Num'Small_Denominator
217 and then Num'Small_Denominator <= 2**59)
218 or else
219 (Num'Small_Denominator < Num'Small_Numerator
220 and then Num'Small_Numerator <= 2**53));
221 -- These conditions are derived from the prerequisites of System.Image_F
222
223 E : constant Natural := 63 - 32 * Boolean'Pos (OK_Put_32);
224 -- T'Size - 1 for the selected Int{32,64}
225
226 F0 : constant Natural := 0;
227 F1 : constant Natural :=
228 F0 + 18 * Boolean'Pos (2.0**E * Num'Small * 10.0**(-F0) >= 1.0E+18);
229 F2 : constant Natural :=
230 F1 + 9 * Boolean'Pos (2.0**E * Num'Small * 10.0**(-F1) >= 1.0E+9);
231 F3 : constant Natural :=
232 F2 + 5 * Boolean'Pos (2.0**E * Num'Small * 10.0**(-F2) >= 1.0E+5);
233 F4 : constant Natural :=
234 F3 + 3 * Boolean'Pos (2.0**E * Num'Small * 10.0**(-F3) >= 1.0E+3);
235 F5 : constant Natural :=
236 F4 + 2 * Boolean'Pos (2.0**E * Num'Small * 10.0**(-F4) >= 1.0E+2);
237 F6 : constant Natural :=
238 F5 + 1 * Boolean'Pos (2.0**E * Num'Small * 10.0**(-F5) >= 1.0E+1);
239 -- Binary search for the number of digits - 1 before the decimal point of
240 -- the product 2.0**E * Num'Small.
241
242 For0 : constant Natural := 2 + F6;
243 -- Fore value for the fixed point type whose mantissa is Int{32,64} and
244 -- whose small is Num'Small.
245
246 ---------
247 -- Get --
248 ---------
249
250 procedure Get
251 (File : File_Type;
252 Item : out Num;
253 Width : Field := 0)
254 is
255 pragma Unsuppress (Range_Check);
256
257 begin
258 if OK_Get_32 then
259 Item := Num'Fixed_Value
260 (Aux32.Get (File, Width,
261 -Num'Small_Numerator,
262 -Num'Small_Denominator));
263 elsif OK_Get_64 then
264 Item := Num'Fixed_Value
265 (Aux64.Get (File, Width,
266 -Num'Small_Numerator,
267 -Num'Small_Denominator));
268 else
269 Float_Aux.Get (File, Long_Long_Float (Item), Width);
270 end if;
271
272 exception
273 when Constraint_Error => raise Data_Error;
274 end Get;
275
276 procedure Get
277 (Item : out Num;
278 Width : Field := 0)
279 is
280 begin
281 Get (Current_Input, Item, Width);
282 end Get;
283
284 procedure Get
285 (From : String;
286 Item : out Num;
287 Last : out Positive)
288 is
289 pragma Unsuppress (Range_Check);
290
291 begin
292 if OK_Get_32 then
293 Item := Num'Fixed_Value
294 (Aux32.Gets (From, Last,
295 -Num'Small_Numerator,
296 -Num'Small_Denominator));
297 elsif OK_Get_64 then
298 Item := Num'Fixed_Value
299 (Aux64.Gets (From, Last,
300 -Num'Small_Numerator,
301 -Num'Small_Denominator));
302 else
303 Float_Aux.Gets (From, Long_Long_Float (Item), Last);
304 end if;
305
306 exception
307 when Constraint_Error => raise Data_Error;
308 end Get;
309
310 ---------
311 -- Put --
312 ---------
313
314 procedure Put
315 (File : File_Type;
316 Item : Num;
317 Fore : Field := Default_Fore;
318 Aft : Field := Default_Aft;
319 Exp : Field := Default_Exp)
320 is
321 begin
322 if OK_Put_32 then
323 Aux32.Put (File, Int32'Integer_Value (Item), Fore, Aft, Exp,
324 -Num'Small_Numerator, -Num'Small_Denominator,
325 For0, Num'Aft);
326 elsif OK_Put_64 then
327 Aux64.Put (File, Int64'Integer_Value (Item), Fore, Aft, Exp,
328 -Num'Small_Numerator, -Num'Small_Denominator,
329 For0, Num'Aft);
330 else
331 Float_Aux.Put (File, Long_Long_Float (Item), Fore, Aft, Exp);
332 end if;
333 end Put;
334
335 procedure Put
336 (Item : Num;
337 Fore : Field := Default_Fore;
338 Aft : Field := Default_Aft;
339 Exp : Field := Default_Exp)
340 is
341 begin
342 Put (Current_Out, Item, Fore, Aft, Exp);
343 end Put;
344
345 procedure Put
346 (To : out String;
347 Item : Num;
348 Aft : Field := Default_Aft;
349 Exp : Field := Default_Exp)
350 is
351 begin
352 if OK_Put_32 then
353 Aux32.Puts (To, Int32'Integer_Value (Item), Aft, Exp,
354 -Num'Small_Numerator, -Num'Small_Denominator,
355 For0, Num'Aft);
356 elsif OK_Put_64 then
357 Aux64.Puts (To, Int64'Integer_Value (Item), Aft, Exp,
358 -Num'Small_Numerator, -Num'Small_Denominator,
359 For0, Num'Aft);
360 else
361 Float_Aux.Puts (To, Long_Long_Float (Item), Aft, Exp);
362 end if;
363 end Put;
364
365 end Ada.Text_IO.Fixed_IO;
This page took 0.052506 seconds and 5 git commands to generate.