Skip to content

Commit 5d069df

Browse files
committed
Fix multiplication and division of complex numbers
Multiplication and division of complex numbers are not just pointwise applications of those operations. Fixes: diffblue#8375
1 parent 629dbcd commit 5d069df

File tree

3 files changed

+79
-34
lines changed

3 files changed

+79
-34
lines changed

regression/cbmc/complex2/main.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <complex.h>
2+
3+
int main()
4+
{
5+
char choice;
6+
float re = choice ? 1.3f : 2.1f; // a non-constant well-behaved float
7+
float complex z1 = I + re;
8+
float complex z2 = z1 * z1;
9+
float complex expected = 2 * I * re + re * re - 1; // (a+i)^2 = 2ai + a^2 - 1
10+
float complex actual =
11+
re * re + I; // (a1 + b1*i)*(a2 + b2*i) = (a1*a2 + b1*b2*i)
12+
__CPROVER_assert(z2 == expected, "right");
13+
__CPROVER_assert(z2 == actual, "wrong");
14+
return 0;
15+
}

regression/cbmc/complex2/test.desc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/solvers/flattening/boolbv_floatbv_op.cpp

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -131,44 +131,66 @@ bvt boolbvt::convert_floatbv_op(const ieee_float_op_exprt &expr)
131131
sub_width > 0 && width % sub_width == 0,
132132
"width of a complex subtype must be positive and evenly divide the "
133133
"width of the complex expression");
134+
DATA_INVARIANT(
135+
sub_width * 2 == width, "a complex type consists of exactly two parts");
136+
137+
bvt lhs_real{lhs_as_bv.begin(), lhs_as_bv.begin() + sub_width};
138+
bvt rhs_real{rhs_as_bv.begin(), rhs_as_bv.begin() + sub_width};
134139

135-
std::size_t size=width/sub_width;
136-
bvt result_bv;
137-
result_bv.resize(width);
140+
bvt lhs_imag{lhs_as_bv.begin() + sub_width, lhs_as_bv.end()};
141+
bvt rhs_imag{rhs_as_bv.begin() + sub_width, rhs_as_bv.end()};
138142

139-
for(std::size_t i=0; i<size; i++)
143+
bvt result_real, result_imag;
144+
145+
if(expr.id() == ID_floatbv_plus || expr.id() == ID_floatbv_minus)
146+
{
147+
result_real = float_utils.add_sub(
148+
lhs_real, rhs_real, expr.id() == ID_floatbv_minus);
149+
result_imag = float_utils.add_sub(
150+
lhs_imag, rhs_imag, expr.id() == ID_floatbv_minus);
151+
}
152+
else if(expr.id() == ID_floatbv_mult)
153+
{
154+
// Could be optimised to just three multiplications with more additions
155+
// instead, but then we'd have to worry about the impact of possible
156+
// overflows. So we use the naive approach for now:
157+
result_real = float_utils.add_sub(
158+
float_utils.mul(lhs_real, rhs_real),
159+
float_utils.mul(lhs_imag, rhs_imag),
160+
true);
161+
result_imag = float_utils.add_sub(
162+
float_utils.mul(lhs_real, rhs_imag),
163+
float_utils.mul(lhs_imag, rhs_real),
164+
false);
165+
}
166+
else if(expr.id() == ID_floatbv_div)
140167
{
141-
bvt lhs_sub_bv, rhs_sub_bv, sub_result_bv;
142-
143-
lhs_sub_bv.assign(
144-
lhs_as_bv.begin() + i * sub_width,
145-
lhs_as_bv.begin() + (i + 1) * sub_width);
146-
rhs_sub_bv.assign(
147-
rhs_as_bv.begin() + i * sub_width,
148-
rhs_as_bv.begin() + (i + 1) * sub_width);
149-
150-
if(expr.id()==ID_floatbv_plus)
151-
sub_result_bv = float_utils.add_sub(lhs_sub_bv, rhs_sub_bv, false);
152-
else if(expr.id()==ID_floatbv_minus)
153-
sub_result_bv = float_utils.add_sub(lhs_sub_bv, rhs_sub_bv, true);
154-
else if(expr.id()==ID_floatbv_mult)
155-
sub_result_bv = float_utils.mul(lhs_sub_bv, rhs_sub_bv);
156-
else if(expr.id()==ID_floatbv_div)
157-
sub_result_bv = float_utils.div(lhs_sub_bv, rhs_sub_bv);
158-
else
159-
UNREACHABLE;
160-
161-
INVARIANT(
162-
sub_result_bv.size() == sub_width,
163-
"we constructed a new complex of the right size");
164-
INVARIANT(
165-
i * sub_width + sub_width - 1 < result_bv.size(),
166-
"the sub-bitvector fits into the result bitvector");
167-
std::copy(
168-
sub_result_bv.begin(),
169-
sub_result_bv.end(),
170-
result_bv.begin() + i * sub_width);
168+
bvt numerator_real = float_utils.add_sub(
169+
float_utils.mul(lhs_real, rhs_real),
170+
float_utils.mul(lhs_imag, rhs_imag),
171+
false);
172+
bvt numerator_imag = float_utils.add_sub(
173+
float_utils.mul(lhs_imag, rhs_real),
174+
float_utils.mul(lhs_real, rhs_imag),
175+
true);
176+
177+
bvt denominator = float_utils.add_sub(
178+
float_utils.mul(rhs_real, rhs_real),
179+
float_utils.mul(rhs_imag, rhs_imag),
180+
false);
181+
182+
result_real = float_utils.div(numerator_real, denominator);
183+
result_imag = float_utils.div(numerator_imag, denominator);
171184
}
185+
else
186+
UNREACHABLE;
187+
188+
bvt result_bv = std::move(result_real);
189+
result_bv.reserve(width);
190+
result_bv.insert(
191+
result_bv.end(),
192+
std::make_move_iterator(result_imag.begin()),
193+
std::make_move_iterator(result_imag.end()));
172194

173195
return result_bv;
174196
}

0 commit comments

Comments
 (0)