编译器选择'Small的演示
作为Ada中固定点类型的新手,听到'Small的默认值是小于或等于指定delta的2的幂次方时,我感到惊讶。以下是一个简短的片段来介绍这个问题:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type foo is delta 0.1 range 0.0..1.0;
x : foo := foo'delta;
begin
Put (x'Image);
while true loop
x := x + foo'delta;
Put (x'Image);
end loop;
end Main;
输出结果表明,“Small确实是小于0.1的最大2的幂,因为一些打印出来的值出现了两次:”
0.1 0.1 0.2 0.3 0.3 0.4 0.4 0.5 0.6 0.6 0.7 0.8 0.8 0.9 0.9 1.0
raised CONSTRAINT_ERROR : main.adb:9 range check failed
解决方案:将它们指定为相同的值
如果我们真的想要0.1作为差值,我们可以这样说:
real_delta : constant := 0.1;
type foo is delta real_delta range 0.0..1.0
with Small => real_delta;
问题:指定两个不同的值是否有用?
如果优化是唯一的用例,则可以将其作为布尔属性,或者只是一个警告“选择的增量不是2的幂(建议使用2 ** -4)”。是否有任何原因指定两个不同的值,例如:
type foo is delta 0.1 range 0.0..1.0
with Small => 0.07;
x : foo := 0.4 + 0.4; -- equals 0.7 rather than 0.8
这似乎只会让后来遇到这个问题的读者更加困惑。以下示例摘自John Barnes的《Ada 2012编程》第17.5节第434页。他没有解释为什么增量(delta)是一个远大于实际“Small used”值的值,而不是它的倍数。
π : constant := Ada.numerics.π;
type angle is delta 0.1 range -4*π .. 4 *π;
for Angle'Small use π * 2.0**(-13);
我看到的唯一区别是“图像现在只打印一位数字精度。那是唯一的区别吗?
此外,为什么我的编译器会拒绝for foo'Small use foo'Delta
?
我遇到了直接执行上述操作而不使用常量的代码:
type foo is delta 0.1 range 0.0..1.0;
for foo'Small use foo'Delta;
但 GNAT 在声明 foo 后立即报告其被冻结:
main.adb:6:04: representation item appears too late
这个在Ada的某个版本中改变了吗?它应该是有效的Ada2012吗?
foo'Delta
分配给foo'Small
,所以表示子句被拒绝。因此,编译器会冻结类型foo
(包括类型的 small 部分)以计算(固定)foo'Delta
的值,但是一旦确定了值foo'Delta
,由于foo
已经被冻结,赋值就不再可能了。 - DeeDeeFoo_Delta := 0.1;
(当然,0.1是一个非常大的值)。 - Simon Wright