Sono nuovo di Ada e ho provato i tipi "delta" a virgola fissa. In particolare, ho creato un intervallo di tipo delta a 32 bit 0.0 .. 1.0. Tuttavia, quando provo a quadrare determinati valori, ottengo un CONSTRAINT_ERROR. Per quanto ne so, questo non dovrebbe accadere con la mia gamma specificata. La soglia per questo errore sembra essere sqrt(1/2)
. Sto usando GNAT da MinGW-w64 versione 4.8.0.Tipo di punto fisso che non si moltiplica correttamente
Codice di prova (tutti compila in forma di gnatmake <file>
senza avvisi/errori):
types.ads:
pragma Ada_2012;
with Ada.Unchecked_Conversion;
with Ada.Text_IO;
package Types is
type Fixed_Type is delta 1.0/2**32 range 0.0 .. 1.0
with Size => 32;
type Modular_Type is mod 2**32
with Size => 32;
function Fixed_To_Mod is new Ada.Unchecked_Conversion(Fixed_Type, Modular_Type);
package MIO is new Ada.Text_IO.Modular_IO(Modular_Type);
package FIO is new Ada.Text_IO.Fixed_IO(Fixed_Type);
end Types;
specifics.adb:
pragma Ada_2012;
with Ada.Text_IO;
with Types; use Types;
procedure Specifics is
package TIO renames Ada.Text_IO;
procedure TestValue(val: in Fixed_Type) is
square : Fixed_Type;
begin
square := val * val;
TIO.Put_Line("Value " & Fixed_Type'Image(val) & " squares properly.");
TIO.Put_Line("Square: " & Fixed_Type'Image(square));
TIO.New_Line;
exception
when Constraint_Error =>
TIO.Put_Line("Value " & Fixed_Type'Image(val) & " does not square properly.");
TIO.Put_Line("Square: " & Fixed_Type'Image(val * val));
TIO.Put_Line("Not sure how that worked.");
TIO.New_Line;
end TestValue;
function ParseFixed(s: in String; last: in Natural; val: out Fixed_Type) return Boolean is
l : Natural;
begin
FIO.Get(s(s'First..last), val, l);
return TRUE;
exception
when others =>
TIO.Put_Line("Parsing failed.");
return FALSE;
end ParseFixed;
buffer : String(1..20);
last : Natural;
f : Fixed_Type;
begin
loop
TIO.Put(">>> ");
TIO.Get_Line(buffer, last);
exit when buffer(1..last) = "quit";
if ParseFixed(buffer, last, f) then
TestValue(f);
end if;
end loop;
end Specifics;
uscita di specifics.adb:
>>> 0.1
Value 0.1000000001 squares properly.
Square: 0.0100000000
>>> 0.2
Value 0.2000000000 squares properly.
Square: 0.0399999998
>>> 0.4
Value 0.3999999999 squares properly.
Square: 0.1599999999
>>> 0.6
Value 0.6000000001 squares properly.
Square: 0.3600000001
>>> 0.7
Value 0.7000000000 squares properly.
Square: 0.4899999998
>>> 0.75
Value 0.7500000000 does not square properly.
Square: -0.4375000000
Not sure how that worked.
>>> quit
In qualche modo, moltiplicando val
da solo ha dato un numero negativo, che spiega il CONSTRAINT_ERROR ... ma non importa, perché sto ricevendo un numero negativo in primo luogo?
Ho quindi deciso di verificare il punto in cui squadratura i numeri hanno cominciato in mancanza, così ho scritto il seguente frammento:
fixedpointtest.adb:
pragma Ada_2012;
with Ada.Text_IO;
with Types; use Types;
procedure FixedPointTest is
package TIO renames Ada.Text_IO;
test, square : Fixed_Type := 0.0;
begin
while test /= Fixed_Type'Last loop
square := test * test;
test := test + Fixed_Type'Delta;
end loop;
exception
when Constraint_Error =>
TIO.Put_Line("Last valid value: " & Fixed_Type'Image(test-Fixed_Type'Delta));
TIO.Put("Hex value: ");
MIO.Put(Item => Fixed_To_Mod(test-Fixed_Type'Delta), Base => 16);
TIO.New_Line;
TIO.Put("Binary value: ");
MIO.Put(Item => Fixed_To_Mod(test-Fixed_Type'Delta), Base => 2);
TIO.New_Line;
TIO.New_Line;
TIO.Put_Line("First invalid value: " & Fixed_Type'Image(test));
TIO.Put("Hex value: ");
MIO.Put(Item => Fixed_To_Mod(test), Base => 16);
TIO.New_Line;
TIO.Put("Binary value: ");
MIO.Put(Item => Fixed_To_Mod(test), Base => 2);
TIO.New_Line;
TIO.New_Line;
end FixedPointTest;
e ottenuto il seguente risultato:
Last valid value: 0.7071067810
Hex value: 16#B504F333#
Binary value: 2#10110101000001001111001100110011#
First invalid value: 0.7071067812
Hex value: 16#B504F334#
Binary value: 2#10110101000001001111001100110100#
Quindi, sqrt(1/2)
, ci incontriamo di nuovo. Qualcuno potrebbe spiegarmi perché il mio codice sta facendo questo? C'è un modo per farlo moltiplicare correttamente?
Vale la pena stampare anche i valori esadecimali e binari dei * quadrati * degli ultimi valori validi e primi non validi. Sembra un bug in cui l'implementazione prende la scorciatoia di usare un intero (firmato) a 32 bit "sotto il cofano". Sarei propenso a provare delta = 1.0/2 ** 31 e 1.0/2 ** 33 (stesso intervallo). Quest'ultimo potrebbe forzare un tipo interno più ampio o non riuscire a compilare. –
Ho provato quei delta, ed entrambi funzionano splendidamente (il secondo solo quando si rimuove la clausola 'with Size => 32', altrimenti si compila l'errore). Per il delta originale, mi sono chiesto perché il programma non ha generato un 'CONSTRAINT_ERROR' ogni volta che ho provato ad assegnare un valore> = 0.5. Tuttavia, quando ricompilato con '-gnato', ho scoperto che in realtà non accetta tali valori. C'è un modo per eliminare il bit del segno o sono obbligato a utilizzare un delta diverso? – ericmaht