From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on ip-172-31-65-14.ec2.internal X-Spam-Level: X-Spam-Status: No, score=-3.2 required=3.0 tests=BAYES_00,NICE_REPLY_A, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 Path: eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: GNAT Community 2020 (20200818-93): Big_Integer Date: Fri, 30 Jun 2023 23:07:10 +0200 Organization: A noiseless patient Spider Message-ID: References: <55561d4b-8288-4770-a7b5-681567752ab0n@googlegroups.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Injection-Date: Fri, 30 Jun 2023 21:07:10 -0000 (UTC) Injection-Info: dont-email.me; posting-host="cd87d6d5da359623298485c320aae58c"; logging-data="2772383"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18WpkNmgJl3DtcNi1MwaGiwmZt51S8WnhA=" User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Thunderbird/102.12.0 Cancel-Lock: sha1:yX/+5xbDUfGiCkXJnbHagKd53LA= In-Reply-To: <55561d4b-8288-4770-a7b5-681567752ab0n@googlegroups.com> Content-Language: en-US Xref: news.eternal-september.org comp.lang.ada:65389 List-Id: On 2023-06-30 21:28, Frank Jørgen Jørgensen wrote: > I'm running the below program with GNAT Community 2020 (20200818-93) > on Windows 11 Home. > I have some problems trying to save big numbers to a file - so I noticed this behaviour: > If I open Test.dat in Visual Studio Hex editor, it seems like this program saves this big number with a different bit pattern each time. > Is that as expected? > I do have some problems reading back the big numbers in my real code. > When I compile I get the warning: "Ada.Numerics.Big_Numbers.Big_Integers" is an Ada 202x unit. > > -- > with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO; > with Ada.Numerics.Big_Numbers.Big_Integers; > > procedure Test is > > B1 : Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer; > F1 : File_Type; > S1 : Stream_Access; > begin > B1 := 1; > > Ada.Streams.Stream_IO.Create (F1, Out_File, "Test.dat"); > S1 := Ada.Streams.Stream_IO.Stream (F1); > Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer'Write(S1, B1); > Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer'Output(S1, B1); > Ada.Streams.Stream_IO.Close (F1); > end Test; As a general rule, you should never use predefined implementations of stream attributes except for Stream_Element or Character. Anything else you must always override or else not use. If you want to serialize signed integers use some portable format for it. E.g. a chained encoding. Here is a test program for a straightforward implementation of chained store/restore: ------------------------- with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO; with Ada.Numerics.Big_Numbers.Big_Integers; with Ada.Exceptions; with Ada.IO_Exceptions; procedure Test is use Ada.Streams; use Ada.Numerics.Big_Numbers.Big_Integers; use Ada.Exceptions; use Ada.Streams.Stream_IO; Two : constant Big_Integer := To_Big_Integer (2); package Conversions is new Unsigned_Conversions (Stream_Element); use Conversions; function Get ( Stream : in out Root_Stream_Type'Class ) return Big_Integer is Result : Big_Integer; Power : Natural := 6; Negative : Boolean; Buffer : Stream_Element_Array (1..1); Last : Stream_Element_Offset; This : Stream_Element renames Buffer (1); begin Stream.Read (Buffer, Last); if Last /= 1 then raise End_Error; end if; Result := To_Big_Integer ((This and 2#0111_1110#) / 2); Negative := 0 /= (This and 1); if 0 = (This and 16#80#) then if Negative then return -Result - 1; else return Result; end if; end if; loop Stream.Read (Buffer, Last); if Last /= 1 then raise End_Error; end if; Result := Result + Two**Power * To_Big_Integer (This and 16#7F#); if 0 = (This and 16#80#) then if Negative then return -Result - 1; else return Result; end if; end if; Power := Power + 7; end loop; end Get; procedure Put ( Stream : in out Root_Stream_Type'Class; Value : Big_Integer ) is Item : Big_Integer := Value; Buffer : Stream_Element_Array (1..1); This : Stream_Element renames Buffer (1); begin if Item >= 0 then Item := Value; This := From_Big_Integer (Item mod (16#40#)) * 2; else Item := -(Value + 1); This := From_Big_Integer (Item mod (16#40#)) * 2 + 1; end if; Item := Item / 16#40#; if Item = 0 then Stream.Write (Buffer); return; end if; This := This or 16#80#; Stream.Write (Buffer); loop This := From_Big_Integer (Item mod 16#80#) or 16#80#; Item := Item / 16#80#; if Item = 0 then This := This and 16#7F#; Stream.Write (Buffer); return; end if; Stream.Write (Buffer); end loop; end Put; F : File_Type; begin Create (F, Out_File, "Test.dat"); for I in -1_000_000..1_000_000 loop Put (Stream (F).all, To_Big_Integer (I)); end loop; Close (F); Open (F, In_File, "Test.dat"); for I in -1_000_000..1_000_000 loop declare Value : constant Big_Integer := Get (Stream (F).all); begin if Value /= To_Big_Integer (I) then raise Data_Error; end if; end; end loop; Close (F); end Test; ------------------------- The above could be optimized to work with buffers rather than reading/writing stream octets one by one. It is a long story, but normally you would implement some data blocks with the length count on top of the stream in order to avoid inefficient octet by octet reading and add an error correction layer. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de