我有以下程序:
procedure Main with SPARK_Mode is
F : array (0 .. 10) of Integer := (0, 1, others => 0);
begin
for I in 2 .. F'Last loop
F (I) := F (I - 1) + F (I - 2);
end loop;
end Main;
如果我运行gnatprove
,我会得到以下结果,指向+
符号:
medium:溢出检查可能会失败
这是否意味着F (I - 1)
可能等于Integer'Last
,并且添加任何内容会溢出?如果是这样,那么从程序流程中不清楚这是不可能的吗?或者我需要在合同中指定这个吗?如果没有,那么它是什么意思?
一个反例表明,在这种情况下gnatprove
确实担心Integer
的边缘:
medium:溢出检查可能会失败(例如
F = (1 => -1, others => -2147483648)
和I = 2
)
答案 0 :(得分:2)
考虑为代码添加循环不变量。以下是“使用Spark构建高完整性应用程序”一书中的一个示例。
procedure Copy_Into(Buffer : out Buffer_Type;
Source : in String) is
Characters_To_Copy : Buffer.Count_Type := Maximum_Buffer_Size;
begin
Buffer := (Others => ' '); -- Initialize to all blanks
if Source'Length < Characters_To_Copy then
Characters_To_Copy := Source'Length;
end if;
for Index in Buffer.Count_Type range 1..Characters_To_Copy loop
pragma Loop_Invariant
(Characters_To_Copy <= Source'Length and
Characters_To_Copy = Characters_To_Copy'Loop_Entry);
Buffer (Index) := Source(Source'First + (Index - 1));
end loop;
end Copy_Into;
答案 1 :(得分:0)
这个循环不变量应该起作用 - 因为2 ^(n-1)+ 2 ^(n-2)&lt; 2 ^ n - 但我无法说服投标人:
procedure Fibonacci with SPARK_Mode is
F : array (0 .. 10) of Natural := (0 => 0,
1 => 1,
others => 0);
begin
for I in 2 .. F'Last loop
pragma Loop_Invariant
(for all J in F'Range => F (J) < 2 ** J);
F (I) := F (I - 1) + F (I - 2);
end loop;
end Fibonacci;
您可以通过一些手动帮助来说服证明者(显示2 ^(n-1)+ 2 ^(n-2)= 2 ^(n-2)*(2 + 1)= 3 / 4 * 2 ^ n <2 ^ n)。
答案 2 :(得分:0)
这已经是一个老问题了,但是我还是想添加一个答案(仅供将来参考)。
随着证明者的进步,问题中所述的示例现在已在GNAT CE 2019中证明是开箱即用的(即无需循环不变性)。还可以证明一个更高级的示例:
main.adb
procedure Main with SPARK_Mode is
-- NOTE: The theoretical upper bound for N is 46 as
--
-- Fib (46) < 2**31 - 1 < Fib (47)
-- 1_836_311_903 < 2_147_483_647 < 2_971_215_073
-- NOTE: Proved with Z3 only. Z3 is pretty good in arithmetic. Additional
-- options for gnatprove:
--
-- --prover=Z3 --steps=0 --timeout=10 --report=all
type Seq is array (Natural range <>) of Natural;
function Fibonacci (N : Natural) return Seq with
Pre => (N in 2 .. 46),
Post => (Fibonacci'Result (0) = 0)
and then (Fibonacci'Result (1) = 1)
and then (for all I in 2 .. N =>
Fibonacci'Result (I) = Fibonacci'Result (I - 1) + Fibonacci'Result (I - 2));
---------------
-- Fibonacci --
---------------
function Fibonacci (N : Natural) return Seq is
F : Seq (0 .. N) := (0, 1, others => 0);
begin
for I in 2 .. N loop
F (I) := F (I - 1) + F (I - 2);
pragma Loop_Invariant
(for all J in 2 .. I =>
F (J) = F (J - 1) + F (J - 2));
-- NOTE: The loop invariant below helps the prover to proof the
-- absence of overflow. It "reminds" the prover that all values
-- from iteration 3 onwards are strictly monotonically increasing.
-- Hence, if absence of overflow is proven in this iteration,
-- then absence is proven for all previous iterations.
pragma Loop_Invariant
(for all J in 3 .. I =>
F (J) > F (J - 1));
end loop;
return F;
end Fibonacci;
begin
null;
end Main;