comp.lang.ada
 help / color / mirror / Atom feed
From: Shark8 <onewingedshark@gmail.com>
Subject: Re: Fixed vs float and precision and conversions
Date: Wed, 8 Jul 2020 09:16:37 -0700 (PDT)	[thread overview]
Message-ID: <518ff68b-1017-4775-8248-c40048b4fe67o@googlegroups.com> (raw)
In-Reply-To: <re3vbb$kca$1@dont-email.me>

On Wednesday, July 8, 2020 at 2:15:43 AM UTC-6, björn lundin wrote:
> Den 2020-07-07 kl. 23:58, skrev Shark8:
> > On Tuesday, July 7, 2020 at 3:10:22 PM UTC-6, björn lundin wrote:
> >>
> >> I have a fixed type
> >>     type Fixed_Type is delta 0.001 digits 18;
> >>
> >> but JSON does not support that. So I get floats instead.
> > This is a limitation of JSON, IIUC: all numeric are IEE754 floats -- see: https://www.json.org/json-en.html
> 
> Yes.
> 
> > 
> > If you have access to both sides of the serialization, you could define an intermediate serialization say a string of "Fixed_Type#value#" where 'value' is the string-representation you need. -- You can extract the value by indexing on the '#' characters, extracting the portion in between, and feeding that via Fixed_Type'Value( EXTRACTED_SUBSTRING ), and produce it via "Fixed_Type#" & Fixed_Type'Image( fp_value ) & '#'.
> 
> Interesting - but for another project.
> Here, I do not own the other side.
> 
> Perhaps I should have stated that the other side is Betfair, and in this 
> case I get odds of horse races via their API.
> The only valid odds are in a fixed set of numbers like
> 
> they have small increments in the beginning, which increase as the odds 
> go up
> like this partial array shows where
>    type Tics_Type is new integer range 1 .. 350;
> 
> Global_Odds_Table : array(Tics_Type'Range) of Fixed_Type := (
> 1.01,   1.02,   1.03,   1.04,   1.05,
> ...
> 2.00,   2.02,   2.04,   2.06,   2.08,   2.10,
> ...
> 3.00,   3.05,   3.10,   3.15,   3.20,   3.25,
> ...
> 4.00,   4.10,   4.20,   4.30,   4.40,   4.50,
> ...
> 5.00,   5.10,   5.20,   5.30,   5.40,   5.50,
> ...
> 10.00,  10.50,  11.00,  11.50,  12.00,  12.50,
> ...
> 20.00,  21.00,  22.00,  23.00,  24.00,  25.00,
> ...
> 50.00,  55.00,  60.00,  65.00,  70.00,  75.00,
> ...
> 100.00, 110.00, 120.00, 130.00, 140.00, 150.00,
> ...
> 960.00, 970.00, 980.00, 990.00,1000.00);
> 
> 
> so I get 5.1 from the JSON, which is occasionaly converted to 5.099, 
> which I have trouble to find in the array above.
> I am now thinking of traversing it and make an absolute diff like 
> abs(myval - arrayval) and pick the one with smallest diff.
> 
> But my idea of using fixed was to escape the rounding errors.
> But hmm, here it is about feeding data _into_ the system, and here I'll 
> get the error, while the rest will work nice.
> 
> Hmm, I need to do some testing
> 
> Thanks for the reply
> 
> 
> -- 
> Björn

Ok, so one thing to keep in mind here is that type-wise you're dealing not with two types, but *three* -- the type on the other side, the values in the Global_Odds_Table; the IEEE754 transport, via JSON serialization and deserialization; and the type on your side, Fixed_Type.

So then, the correct way to set up your end of things is to have a Fixed_Type which only has the values of Global_Odds_Table for your general-purpose/internal-usage, and have a conversion function for handling the IEEE754; here you are:

    Lowest_Odds  : Constant := 1.01;
    Highest_Odds : Constant := 1_000.00;
    type Odds_Base is delta 0.001 digits 18 range Lowest_Odds..Highest_Odds;
    Subtype Odds is Odds_Base -- range Lowest_Odds..Hihgest_Odds
      with Predicate_Failure => raise Constraint_Error with
        Odds_Base'Image(Odds) & " is not a valid value.",
      Static_Predicate => Odds in
        --    Global_Odds_Table
          1.01 |   1.02 |   1.03 |   1.04 |   1.05 |
          2.00 |   2.02 |   2.04 |   2.06 |   2.08 |   2.10 |
          3.00 |   3.05 |   3.10 |   3.15 |   3.20 |   3.25 |
          4.00 |   4.10 |   4.20 |   4.30 |   4.40 |   4.50 |
          5.00 |   5.10 |   5.20 |   5.30 |   5.40 |   5.50 |
         10.00 |  10.50 |  11.00 |  11.50 |  12.00 |  12.50 |
         20.00 |  21.00 |  22.00 |  23.00 |  24.00 |  25.00 |
         50.00 |  55.00 |  60.00 |  65.00 |  70.00 |  75.00 |
        100.00 | 110.00 | 120.00 | 130.00 | 140.00 | 150.00 |
        960.00 | 970.00 | 980.00 | 990.00 |1000.00 ;
    
    Function Convert( Input : Interfaces.IEEE_Float_32 ) return Odds is
        use Interfaces;
    Begin
        Ada.Text_IO.Put_Line( "Input:" & Input'Image );
        Ada.Text_IO.Put_Line( "OB:" & Odds_Base'Round(Input)'Image );
        Return Odds'Round( Input );
    Exception
        When Constraint_Error =>
            -- Handle out-of-range errors.
            if Input < Lowest_Odds then
                return Lowest_Odds;
            elsif Input > Highest_Odds then
                return Highest_Odds;
            else
                declare
                    --    Global_Odds_Table
                    Table : Constant Array (Positive range <>) of IEEE_Float_32 :=
                      ( 1.01,   1.02,   1.03,   1.04,   1.05,
                        2.00,   2.02,   2.04,   2.06,   2.08,   2.10,
                        3.00,   3.05,   3.10,   3.15,   3.20,   3.25,
                        4.00,   4.10,   4.20,   4.30,   4.40,   4.50,
                        5.00,   5.10,   5.20,   5.30,   5.40,   5.50,
                        10.00,  10.50,  11.00,  11.50,  12.00,  12.50,
                        20.00,  21.00,  22.00,  23.00,  24.00,  25.00,
                        50.00,  55.00,  60.00,  65.00,  70.00,  75.00,
                        100.00, 110.00, 120.00, 130.00, 140.00, 150.00,
                        960.00, 970.00, 980.00, 990.00,1000.00 );
                    Closest_Index : Positive      := 1;
                    Closest_Value : IEEE_Float_32 := IEEE_Float_32'Last;
                begin
                    For X in Table'Range loop
                        if abs (Input - Table(X)) < Closest_Value then
                            Closest_Value:= abs (Input - Table(X));
                            Closest_Index:= X;
                        end if;
                    end loop;
                    
                    Return  Odds'Round( Table(Closest_Index) );
                end;
            end if;
    End Convert;

The bad thing here is that you have to repeat Global_Odds_Table, though this isn't so bad as the second is fully contained within the exception-handler; further, searching the table is done only when 'Round cannot be used directly. Also, this version does catch out of bounds errors and coerce them to the nearest bound. -- You might want to write a one-off program to generate the Predicate-form and array-value form of Global_Odds_Table, since there's 350 values.

  reply	other threads:[~2020-07-08 16:16 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-07 21:10 Fixed vs float and precision and conversions Björn Lundin
2020-07-07 21:30 ` Niklas Holsti
2020-07-08  8:17   ` Björn Lundin
2020-07-07 21:58 ` Shark8
2020-07-08  8:15   ` Björn Lundin
2020-07-08 16:16     ` Shark8 [this message]
2020-07-08 18:08       ` Björn Lundin
2020-07-08 18:10         ` Björn Lundin
2020-07-08 18:21           ` Niklas Holsti
2020-07-08 19:39             ` Björn Lundin
2020-07-08 20:34               ` Niklas Holsti
2020-07-08 21:24                 ` Björn Lundin
2020-07-09  7:11                   ` Niklas Holsti
2020-07-08 18:36           ` Dmitry A. Kazakov
2020-07-08 19:41             ` Björn Lundin
2020-07-09  6:20               ` Dmitry A. Kazakov
2020-07-09  7:23                 ` Björn Lundin
2020-07-09  7:49                   ` Dmitry A. Kazakov
2020-07-08 21:16         ` Shark8
2020-07-08 21:47           ` Björn Lundin
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox