"README.md" did not exist on "ca9b03787e77dd7fd2d6829ae7c7ca4dc1d3d927"
Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
---
layout: post
author: Pascal Cuoq
date: 2011-09-12 05:38 +0200
categories: floating-point value value-builtins
format: xhtml
title: "Better is the enemy of good... sometimes"
summary:
---
{% raw %}
<p>This post is about widening. This technique was shown in the second part of
a <a href="/index.php?post/2011/01/27/On-memcpy-1">previous post about memcpy()</a>
where it was laboriously used to analyze imprecisely
function <code>memcpy()</code> as it is usually written.
The value analysis in Frama-C has the ability to summarize loops
in less time than they would take to execute. Indeed it can
analyze in finite time loops for which it is not clear whether
they terminate at all. This is the result of widening the
voluntary loss of precision in loops.
As a side-effect widening can produce some
counter-intuitive results. This post is about these
counter-intuitive results.</p>
<h2>The expected: better information in better information out</h2>
<p>Users generally expect that
the more you know about the memory state before
the interpretation of a piece of program the more you should
know about the memory state after this piece of program.
Users are right to expect this and indeed it is generally true:</p>
<pre>int u;
main(){
u = Frama_C_interval(20 80);
if (u & 1)
u = 3 * u + 1; // odd
else
u = u / 2; // even
}
</pre>
<p>The command <code>frama-c -val c.c share/builtin.c</code> shows the final value of <code>u</code> to be in <code>[10..241]</code>.
If the first line of <code>main()</code> is changed to make the input value range
over <code>[40..60]</code> instead of <code>[20..80]</code> then the output
interval for <code>u</code> becomes <code>[20..181]</code>. This seems normal :
you know more about variable <code>u</code> going in (all the values in <code>[40..60]</code>
are included in <code>[20..80]</code>) so you know more about <code>u</code>
coming out (<code>[20..181]</code> is included in <code>[10..241]</code>).
Most of the analyzer's weaknesses such as in this case the inability
to recognize that the largest value that can get multiplied by 3 is
respectively 79 or 59 do not compromise this general principle. But widening
does.</p>
<h2>Better information in worse information out</h2>
<p>Widening happens when there is a loop in the program such as in
the example below:</p>
<pre>int u i;
main(){
u = Frama_C_interval(40 60);
for (i=0; i<10; i++)
u = (25 + 3 * u) / 4;
}
</pre>
<pre>$ frama-c -val loop.c share/builtin.c
...
[value] Values for function main:
u ∈ [15..60]
</pre>
<p>Analyzing this program with the
default options produce the interval <code>[15..60]</code> for <code>u</code>.
Now if we change the initial set for <code>u</code> to
be <code>[25..60]</code> an interval that contains all the values
from <code>[40..60]</code> and more:</p>
<pre>$ frama-c -val loop.c share/builtin.c
...
[value] Values for function main:
u ∈ [25..60]
</pre>
<p>By using a larger less precise set as the input of this analysis
we obtain a more precise result. This is counter-intuitive
although it is rarely noticed in practice.</p>
<h2>Better modelization worse analysis</h2>
<p>There are several ways to make this unpleasant situation less likely to
be noticed. Only a few of these are implemented in Frama-C's
value analysis. Still it was difficult enough to find a short
example to illustrate my next point and the program is
only as short as I was able to make it. Consider the following
program:</p>
<pre>int i j;
double ftmp1;
double Frama_C_cos(double);
double Frama_C_cos_precise(double);
#define TH 0.196349540849361875
#define NBC1 14
#define S 0.35355339
double cos(double x)
{
return Frama_C_cos(x);
}
main(){
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
ftmp1 = ((j == 0) ? S : 0.5) *
cos ((2.0 * i + 1.0) * j * TH);
ftmp1 *= (1 << NBC1);
}
}
</pre>
<p>If you analyze the above program with a smartish
modelization of function <code>cos()</code> one that recognizes
when its input is an interval <code>[l..u]</code> of floating-point
values on which <code>cos()</code> is monotonous and computes
the optimal output in this case you get
the following results:</p>
<pre>$ frama-c -val idct.c share/builtin.c
...
ftmp1 ∈ [-3.40282346639e+38 .. 8192.]
</pre>
<p>Replacing the <code>cos()</code> modelization by
a more naive one that returns <code>[-1. .. 1.]</code>
as soon as its input set isn't a singleton you get
the improved result:</p>
<pre>$ frama-c -val idct.c share/builtin.c
...
ftmp1 ∈ [-8192. .. 8192.]
</pre>
<p>This is slightly different from the <code>[25..60]</code>
example in that here we are not changing the input
set but the modelization of one of the functions involved
in the program. Still the causes are the same and the
effects just as puzzling when they are noticeable.
And this is why until version Carbon only
the naive modelization of <code>cos()</code> was
provided.</p>
<p>In the next release there will be in addition to the naive versions <code>Frama_C_cos_precise()</code> and
<code>Frama_C_sin_precise()</code> built-ins for when you want them. Typically this will be for the more precise analyses that do not involve widening.</p>
<p>This post is about widening. This technique was shown in the second part of
a <a href="/index.php?post/2011/01/27/On-memcpy-1">previous post about memcpy()</a>
where it was laboriously used to analyze imprecisely
function <code>memcpy()</code> as it is usually written.
The value analysis in Frama-C has the ability to summarize loops
in less time than they would take to execute. Indeed it can
analyze in finite time loops for which it is not clear whether
they terminate at all. This is the result of widening the
voluntary loss of precision in loops.
As a side-effect widening can produce some
counter-intuitive results. This post is about these
counter-intuitive results.</p>
<h2>The expected: better information in better information out</h2>
<p>Users generally expect that
the more you know about the memory state before
the interpretation of a piece of program the more you should
know about the memory state after this piece of program.
Users are right to expect this and indeed it is generally true:</p>
<pre>int u;
main(){
u = Frama_C_interval(20 80);
if (u & 1)
u = 3 * u + 1; // odd
else
u = u / 2; // even
}
</pre>
<p>The command <code>frama-c -val c.c share/builtin.c</code> shows the final value of <code>u</code> to be in <code>[10..241]</code>.
If the first line of <code>main()</code> is changed to make the input value range
over <code>[40..60]</code> instead of <code>[20..80]</code> then the output
interval for <code>u</code> becomes <code>[20..181]</code>. This seems normal :
you know more about variable <code>u</code> going in (all the values in <code>[40..60]</code>
are included in <code>[20..80]</code>) so you know more about <code>u</code>
coming out (<code>[20..181]</code> is included in <code>[10..241]</code>).
Most of the analyzer's weaknesses such as in this case the inability
to recognize that the largest value that can get multiplied by 3 is
respectively 79 or 59 do not compromise this general principle. But widening
does.</p>
<h2>Better information in worse information out</h2>
<p>Widening happens when there is a loop in the program such as in
the example below:</p>
<pre>int u i;
main(){
u = Frama_C_interval(40 60);
for (i=0; i<10; i++)
u = (25 + 3 * u) / 4;
}
</pre>
<pre>$ frama-c -val loop.c share/builtin.c
...
[value] Values for function main:
u ∈ [15..60]
</pre>
<p>Analyzing this program with the
default options produce the interval <code>[15..60]</code> for <code>u</code>.
Now if we change the initial set for <code>u</code> to
be <code>[25..60]</code> an interval that contains all the values
from <code>[40..60]</code> and more:</p>
<pre>$ frama-c -val loop.c share/builtin.c
...
[value] Values for function main:
u ∈ [25..60]
</pre>
<p>By using a larger less precise set as the input of this analysis
we obtain a more precise result. This is counter-intuitive
although it is rarely noticed in practice.</p>
<h2>Better modelization worse analysis</h2>
<p>There are several ways to make this unpleasant situation less likely to
be noticed. Only a few of these are implemented in Frama-C's
value analysis. Still it was difficult enough to find a short
example to illustrate my next point and the program is
only as short as I was able to make it. Consider the following
program:</p>
<pre>int i j;
double ftmp1;
double Frama_C_cos(double);
double Frama_C_cos_precise(double);
#define TH 0.196349540849361875
#define NBC1 14
#define S 0.35355339
double cos(double x)
{
return Frama_C_cos(x);
}
main(){
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
ftmp1 = ((j == 0) ? S : 0.5) *
cos ((2.0 * i + 1.0) * j * TH);
ftmp1 *= (1 << NBC1);
}
}
</pre>
<p>If you analyze the above program with a smartish
modelization of function <code>cos()</code> one that recognizes
when its input is an interval <code>[l..u]</code> of floating-point
values on which <code>cos()</code> is monotonous and computes
the optimal output in this case you get
the following results:</p>
<pre>$ frama-c -val idct.c share/builtin.c
...
ftmp1 ∈ [-3.40282346639e+38 .. 8192.]
</pre>
<p>Replacing the <code>cos()</code> modelization by
a more naive one that returns <code>[-1. .. 1.]</code>
as soon as its input set isn't a singleton you get
the improved result:</p>
<pre>$ frama-c -val idct.c share/builtin.c
...
ftmp1 ∈ [-8192. .. 8192.]
</pre>
<p>This is slightly different from the <code>[25..60]</code>
example in that here we are not changing the input
set but the modelization of one of the functions involved
in the program. Still the causes are the same and the
effects just as puzzling when they are noticeable.
And this is why until version Carbon only
the naive modelization of <code>cos()</code> was
provided.</p>
<p>In the next release there will be in addition to the naive versions <code>Frama_C_cos_precise()</code> and
<code>Frama_C_sin_precise()</code> built-ins for when you want them. Typically this will be for the more precise analyses that do not involve widening.</p>
{% endraw %}