免责声明:所以定点算术是一个相当特殊的话题。这是我对这个话题的理解,但我必须在这里发出警告:我在下面写的内容可能不正确。所以对于所有阅读它的人:如果我错了,请纠正我。
在 Ada 中,真实类型由其准确性定义。这与大多数其他通过实现定义真实类型的语言(即硬件中的表示)相反。在实际类型的定义中选择使用准确度属性而不是表示方面符合语言哲学:准确度作为一个概念,与正确性密切相关;语言的目标。根据准确性定义真实类型也更自然,因为您让编译器根据您对准确性的要求选择最佳类型(在计算机上,所有值都是近似值,您必须一次性处理该事实 -方式或其他方式)。
Delta 属性定义了对与基础定点类型关联的绝对误差界限(准确性)的要求(另请参见Ada 83 Rationale, section 5.1.3)。优点有两个:
2020 年 10 月 24 日更新:这些前几段应在原始语言规范 Ada 83 的上下文中阅读。此外,Ada 83 语言还有第二个重要目标,似乎影响了使用精度定义数字实数类型的选择:分离原则。请参阅 Ada 83 基本原理,chapter 15,以明确说明这意味着什么。然而,在开发 Ada 95 时,逻辑类型属性(如准确性)和机器表示之间的分离(至少对于定点类型)进行了审查,发现在实践中并没有预期的那么有用(参见 Ada 95 基本原理,G.4.2 部分)。因此,从 Ada 95 开始,Delta 属性的作用已经减弱,而Small 属性已被用于制定定点类型和操作的工作方式(例如,参见RM G.2.3 )。
例如,考虑下面的示例程序。该程序定义了一个数字类型,并指定“真”值与底层表示之间的绝对差值不得超过 0.07:
type Fix is delta 0.07 range 0.0 .. 10.0; -- 0.07 is just a random value here
换句话说,当给定的“真”值转换为Fix 类型时,它将获得+/- 0.07 的不确定性。因此,下面程序中的三个命名常量X、Y 和Z,当转换为Fix 类型时,变为:
X : constant := 5.6; -- Becomes 5.6 +/- 0.07 when casted to type Fix.
Y : constant := 0.3; -- Becomes 0.3 +/- 0.07 when casted to type Fix.
Z : constant := 2.5; -- Becomes 2.5 +/- 0.07 when casted to type Fix.
鉴于这些不确定性,人们可以计算一些算术运算序列的结果的不确定性(另请参阅this SO 上的优秀答案)。这实际上在程序中得到了演示。
2020 年 10 月 24 日更新:回想起来,这似乎并不正确,并且存在一些并发症。程序中不确定性的计算不考虑在计算和最终分配期间可能发生的数字的中间和最终转换(量化)。因此,计算的不确定性不正确且过于乐观(即它们应该更大)。我不会删除示例程序,因为它确实为 Delta 属性的原始意图提供了直觉。
使用Long_Float (Flt) 和自定义定点类型Fix 进行了三个计算。当然,使用Long_Float 的计算结果也是一个近似值,但为了演示,我们可以假设它是精确的。然而,定点计算的结果具有(非常)有限的准确性,因为我们为类型Fix 指定了一个相当大的误差范围。另一方面,定点值需要更少的空间(这里:每个值只需 8 位),并且算术运算不需要专门的浮点硬件。
您可以调整Small 属性的事实只是为了允许程序员控制定点类型定义的集合中可用的型号。总是使Small 表示方面等于Delta 属性可能很诱人,但使它们相等不会(通常)改变使用定点时需要执行一些数值(误差)分析的要求数字和算术运算。
2020 年 10 月 24 日更新:我认为这个陈述只是部分正确。 Small 属性确实允许程序员控制型号(即可以由数据类型精确表示的数字),但不仅如此。从 Ada 95 开始,Small 属性在定点算术应该如何工作 (RM G.2.3) 以及大多数关于定点算术和分析定点算法的软件的文档中发挥着重要作用(参见,例如here) 假设硬件中类型的实际表示是已知的;他们对主题的处理并没有偏离绝对误差界限,而是总是偏离定点值的表示。
归根结底,这一切都是关于以数值精度交易资源(内存、浮点硬件)。
2020 年 10 月 24 日更新:此声明还需要备注:在 Ada 中执行定点运算不需要浮点运算取决于上下文。如果操作数的类型和操作的结果对于Small 具有特定值,则可以仅使用整数运算来完成定点运算,特别是乘法和除法。在这里放太多细节,但实际上可以在 GNAT 本身的有据可查的源代码中找到一些有趣的信息,例如,请参阅exp_fixd.adb。
2020 年 10 月 24 日更新:总而言之,鉴于 Ada 95 的变化以及执行定点分析工具的当前最新技术,似乎没有强有力的论据以不同方式选择 Delta 和 Small 的值。 Delta 属性仍然代表绝对误差界限,但它的值并不像最初想象的那么有用。正如您已经提到的,它的唯一主要用途似乎是在定点数据类型(RM 3.5.10 (5) 和 Ada.Text_IO.Fixed_IO)的 I/O 中。
main.adb
pragma Warnings (Off, "static fixed-point value is not a multiple of Small");
pragma Warnings (Off, "high bound adjusted down by delta (RM 3.5.9(13))");
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type Flt is new Long_Float;
type Fix is delta 0.07 range 0.0 .. 10.0;
---------
-- Put --
---------
procedure Put (Nominal, Uncertainty : Flt; Result : Fix) is
package Fix_IO is new Fixed_IO (Fix);
use Fix_IO;
package Flt_IO is new Float_IO (Flt);
use Flt_IO;
begin
Put (" Result will be within : ");
Put (Nominal, Fore => 2, Aft => 4, Exp => 0);
Put (" +/-");
Put (Uncertainty, Fore => 2, Aft => 4, Exp => 0);
New_Line;
Put (" Actual fixed-point result : ");
Put (Result, Fore => 2);
New_Line (2);
end Put;
X : constant := 5.6;
Y : constant := 0.3;
Z : constant := 2.5;
D : constant Flt := Fix'Delta;
begin
Put_Line ("Size of fixed-point type : " & Fix'Size'Image);
Put_Line ("Small of fixed-point type : " & Fix'Small'Image);
New_Line;
-- Update 24-10-2020: Uncertainty computation is too optimistic. It omits
-- the effect of quantization in intermediate and final
-- variable assignments.
Put_Line ("X + Y = ");
Put (Nominal => Flt (X) + Flt (Y),
Uncertainty => D + D,
Result => Fix (X) + Fix (Y));
Put_Line ("X * Y = ");
Put (Nominal => Flt (X) * Flt (Y),
Uncertainty => (D / X + D / Y) * X * Y,
Result => Fix (X) * Fix (Y));
Put_Line ("X * Y + Z = ");
Put (Nominal => Flt (X) * Flt (Y) + Flt (Z),
Uncertainty => (D / X + D / Y) * X * Y + D,
Result => Fix (X) * Fix (Y) + Fix (Z));
end Main;
输出
Size of fixed-point type : 8
Small of fixed-point type : 6.25000000000000000E-02
X + Y =
Result will be within : 5.9000 +/- 0.1400
Actual fixed-point result : 5.81
X * Y =
Result will be within : 1.6800 +/- 0.4130
Actual fixed-point result : 1.38
X * Y + Z =
Result will be within : 4.1800 +/- 0.4830
Actual fixed-point result : 3.88