我是Ada的新手,并且一直在试用定点" delta"类型。具体来说,我创建了一个32位的delta类型范围0.0 .. 1.0。但是,当我尝试对某些值进行平方时,我得到一个CONSTRAINT_ERROR。据我所知,这应该发生在我指定的范围内。此错误的阈值似乎为sqrt(1/2)
。我使用的是MinGW-w64版本4.8.0的GNAT。
测试代码(所有代码都以gnatmake <file>
的形式编译,没有警告/错误):
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;
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
不知何故,将val
乘以它会产生一个负数,这解释了CONSTRAINT_ERROR ......但是没关系,为什么我首先得到一个负数?
然后我决定测试数字开始失败的平方点,所以我写了以下片段:
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;
并获得以下输出:
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#
所以,sqrt(1/2)
,我们再次见面。有人可以向我解释为什么我的代码这样做?有没有办法让它正确繁殖?
答案 0 :(得分:5)
我认为你要求的精确度比实际可用的“精确度”要低1倍。
您的声明
type Fixed_Type is delta 1.0 / 2**32 range 0.0 .. 1.0
with Size => 32;
仅被接受,因为GNAT使用了有偏见的表示;标志位没有空间。您可以看到这一点,因为0.7071067810
表示为16#B504F333#
,其中设置了最重要的位。因此,当您将0.71乘以0.71时,结果具有最高位设置;并且低级代码认为这必须是符号位,所以我们有溢出。
如果您将Fixed_Type
声明为
type Fixed_Type is delta 1.0 / 2**31 range 0.0 .. 1.0
with Size => 32;
一切都应该好。
还有一点:在报告中specifics
的行为输入为0.75时,引用结果
>>> 0.75
Value 0.7500000000 does not square properly.
Square: -0.4375000000
Not sure how that worked.
我使用gnatmake specifics.adb -g -gnato -bargs -E
重建,结果现在是
>>> 0.75
Value 0.7500000000 does not square properly.
Execution terminated by unhandled exception
Exception name: CONSTRAINT_ERROR
Message: 64-bit arithmetic overflow
Call stack traceback locations:
0x100020b79 0x10000ea80 0x100003520 0x100003912 0x10000143e
并且回溯解码为
system__arith_64__raise_error (in specifics) (s-arit64.adb:364)
__gnat_mulv64 (in specifics) (s-arit64.adb:318)
specifics__testvalue.2581 (in specifics) (specifics.adb:20) <<<<<<<<<<
_ada_specifics (in specifics) (specifics.adb:45)
main (in specifics) (b~specifics.adb:246)
和specifics.adb:20
是
TIO.Put_Line("Square: " & Fixed_Type'Image(val * val));
在异常处理程序中,它再次涉及有问题的方块(在异常处理程序中不是一件好事)。您可以看到上面一行中打印的值0.75
没有任何问题:在fixedpointtest.adb
中,添加中没有问题导致上一个有效值0.7071067810
。
我很惊讶地发现-gnato
检测到这个错误,因为我认为它只适用于整数运算;但实际上在GNAT User Guide中有一个讨论,它说它也适用于定点算术。事实证明,您可以使用-gnato3
:
>>> 0.75
Value 0.7500000000 squares properly.
Square: 0.5625000000
但仅以使用任意多精度算术为代价 - 对于时间受限的系统来说不是一个好主意!