Skip to content

Use FloatingPoint theory when using --cprover-smt2 #6132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion regression/cbmc/Float-div2/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE broken-z3-smt-backend
main.c
--floatbv
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float-no-simp6/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--floatbv --no-simplify
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float-no-simp7/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--floatbv --no-simplify
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float20/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--floatbv
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float23/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c

^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float3/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE broken-z3-smt-backend
main.c
--floatbv
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float5/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--floatbv
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float6/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--floatbv
^EXIT=0$
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/Float8/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c

^EXIT=0$
Expand Down
7 changes: 4 additions & 3 deletions src/solvers/smt2/smt2_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ smt2_convt::smt2_convt(
break;

case solvert::CPROVER_SMT2:
use_FPA_theory = true;
use_array_of_bool = true;
use_as_const = true;
use_check_sat_assuming = true;
Expand Down Expand Up @@ -406,23 +407,23 @@ constant_exprt smt2_convt::parse_literal(
{
std::size_t e = unsafe_string2size_t(src.get_sub()[2].id_string());
std::size_t s = unsafe_string2size_t(src.get_sub()[3].id_string());
return ieee_floatt::plus_infinity(ieee_float_spect(s, e)).to_expr();
return ieee_floatt::plus_infinity(ieee_float_spect(s - 1, e)).to_expr();
}
else if(src.get_sub().size()==4 &&
src.get_sub()[0].id()=="_" &&
src.get_sub()[1].id()=="-oo") // (_ -oo e s)
{
std::size_t e = unsafe_string2size_t(src.get_sub()[2].id_string());
std::size_t s = unsafe_string2size_t(src.get_sub()[3].id_string());
return ieee_floatt::minus_infinity(ieee_float_spect(s, e)).to_expr();
return ieee_floatt::minus_infinity(ieee_float_spect(s - 1, e)).to_expr();
}
else if(src.get_sub().size()==4 &&
src.get_sub()[0].id()=="_" &&
src.get_sub()[1].id()=="NaN") // (_ NaN e s)
{
std::size_t e = unsafe_string2size_t(src.get_sub()[2].id_string());
std::size_t s = unsafe_string2size_t(src.get_sub()[3].id_string());
return ieee_floatt::NaN(ieee_float_spect(s, e)).to_expr();
return ieee_floatt::NaN(ieee_float_spect(s - 1, e)).to_expr();
}

if(type.id()==ID_signedbv ||
Expand Down
29 changes: 28 additions & 1 deletion src/solvers/smt2/smt2_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Author: Daniel Kroening, [email protected]

#include <util/arith_tools.h>
#include <util/bitvector_types.h>
#include <util/ieee_float.h>

std::ostream &smt2_format_rec(std::ostream &out, const typet &type)
{
Expand Down Expand Up @@ -92,7 +93,33 @@ std::ostream &smt2_format_rec(std::ostream &out, const exprt &expr)
}
else if(expr_type.id() == ID_floatbv)
{
out << value;
const ieee_floatt v = ieee_floatt(constant_expr);
const size_t e = v.spec.e;
const size_t f = v.spec.f + 1; // SMT-LIB counts the hidden bit

if(v.is_NaN())
{
out << "(_ NaN " << e << " " << f << ")";
}
else if(v.is_infinity())
{
if(v.get_sign())
out << "(_ -oo " << e << " " << f << ")";
else
out << "(_ +oo " << e << " " << f << ")";
}
else
{
// Zero, normal or subnormal

mp_integer binary = v.pack();
std::string binaryString(integer2binary(v.pack(), v.spec.width()));

out << "(fp "
<< "#b" << binaryString.substr(0, 1) << " "
<< "#b" << binaryString.substr(1, e) << " "
<< "#b" << binaryString.substr(1 + e, f - 1) << ")";
}
}
else
DATA_INVARIANT(false, "unhandled constant: " + expr_type.id_string());
Expand Down
142 changes: 111 additions & 31 deletions src/solvers/smt2/smt2_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,10 @@ exprt smt2_parsert::function_application()
if(next_token() != smt2_tokenizert::SYMBOL)
throw error("expected symbol after '_'");

if(has_prefix(smt2_tokenizer.get_buffer(), "bv"))
// copy, the reference won't be stable
const auto id = smt2_tokenizer.get_buffer();

if(has_prefix(id, "bv"))
{
mp_integer i = string2integer(
std::string(smt2_tokenizer.get_buffer(), 2, std::string::npos));
Expand All @@ -515,10 +518,36 @@ exprt smt2_parsert::function_application()

return from_integer(i, unsignedbv_typet(width));
}
else if(id == "+oo" || id == "-oo" || id == "NaN")
{
// These are the "plus infinity", "minus infinity" and NaN
// floating-point literals.
if(next_token() != smt2_tokenizert::NUMERAL)
throw error() << "expected number after " << id;

auto width_e = std::stoll(smt2_tokenizer.get_buffer());

if(next_token() != smt2_tokenizert::NUMERAL)
throw error() << "expected second number after " << id;

auto width_f = std::stoll(smt2_tokenizer.get_buffer());

if(next_token() != smt2_tokenizert::CLOSE)
throw error() << "expected ')' after " << id;

// width_f *includes* the hidden bit
const ieee_float_spect spec(width_f - 1, width_e);

if(id == "+oo")
return ieee_floatt::plus_infinity(spec).to_expr();
else if(id == "-oo")
return ieee_floatt::minus_infinity(spec).to_expr();
else // NaN
return ieee_floatt::NaN(spec).to_expr();
}
else
{
throw error() << "unknown indexed identifier "
<< smt2_tokenizer.get_buffer();
throw error() << "unknown indexed identifier " << id;
}
}
else if(smt2_tokenizer.get_buffer() == "!")
Expand Down Expand Up @@ -699,44 +728,83 @@ exprt smt2_parsert::function_application()
if(next_token() != smt2_tokenizert::CLOSE)
throw error("expected ')' after to_fp");

auto rounding_mode = expression();
// width_f *includes* the hidden bit
const ieee_float_spect spec(width_f - 1, width_e);

if(next_token() != smt2_tokenizert::NUMERAL)
throw error("expected number after to_fp");
auto rounding_mode = expression();

auto real_number = smt2_tokenizer.get_buffer();
auto source_op = expression();

if(next_token() != smt2_tokenizert::CLOSE)
throw error("expected ')' at the end of to_fp");

mp_integer significand, exponent;

auto dot_pos = real_number.find('.');
if(dot_pos == std::string::npos)
// There are several options for the source operand:
// 1) real or integer
// 2) bit-vector, which is interpreted as signed 2's complement
// 3) another floating-point format
if(
source_op.type().id() == ID_real ||
source_op.type().id() == ID_integer)
{
exponent = 0;
significand = string2integer(real_number);
// For now, we can only do this when
// the source operand is a constant.
if(source_op.id() == ID_constant)
{
mp_integer significand, exponent;

const auto &real_number =
id2string(to_constant_expr(source_op).get_value());
auto dot_pos = real_number.find('.');
if(dot_pos == std::string::npos)
{
exponent = 0;
significand = string2integer(real_number);
}
else
{
// remove the '.'
std::string significand_str;
significand_str.reserve(real_number.size());
for(auto ch : real_number)
{
if(ch != '.')
significand_str += ch;
}

exponent =
mp_integer(dot_pos) - mp_integer(real_number.size()) + 1;
significand = string2integer(significand_str);
}

ieee_floatt a(
spec,
static_cast<ieee_floatt::rounding_modet>(
numeric_cast_v<int>(to_constant_expr(rounding_mode))));
a.from_base10(significand, exponent);
return a.to_expr();
}
else
throw error()
<< "to_fp for non-constant real expressions is not implemented";
}
else
else if(source_op.type().id() == ID_unsignedbv)
{
// remove the '.', if any
std::string significand_str;
significand_str.reserve(real_number.size());
for(auto ch : real_number)
if(ch != '.')
significand_str += ch;

exponent = mp_integer(dot_pos) - mp_integer(real_number.size()) + 1;
significand = string2integer(significand_str);
// The operand is hard-wired to be interpreted as a signed number.
return floatbv_typecast_exprt(
typecast_exprt(
source_op,
signedbv_typet(
to_unsignedbv_type(source_op.type()).get_width())),
rounding_mode,
spec.to_type());
}

// width_f *includes* the hidden bit
ieee_float_spect spec(width_f - 1, width_e);
ieee_floatt a(spec);
a.rounding_mode = static_cast<ieee_floatt::rounding_modet>(
numeric_cast_v<int>(to_constant_expr(rounding_mode)));
a.from_base10(significand, exponent);
return a.to_expr();
else if(source_op.type().id() == ID_floatbv)
{
return floatbv_typecast_exprt(
source_op, rounding_mode, spec.to_type());
}
else
throw error() << "unexpected sort given as operand to to_fp";
}
else
{
Expand Down Expand Up @@ -1106,6 +1174,18 @@ void smt2_parsert::setup_expressions()
return with_exprt(op[0], op[1], op[2]);
};

expressions["fp.abs"] = [this] {
auto op = operands();

if(op.size() != 1)
throw error("fp.abs takes one operand");

if(op[0].type().id() != ID_floatbv)
throw error("fp.abs takes FloatingPoint operand");

return abs_exprt(op[0]);
};

expressions["fp.isNaN"] = [this] {
auto op = operands();

Expand Down
11 changes: 11 additions & 0 deletions src/util/ieee_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@ class ieee_floatt
{
}

explicit ieee_floatt(ieee_float_spect __spec, rounding_modet __rounding_mode)
: rounding_mode(__rounding_mode),
spec(std::move(__spec)),
sign_flag(false),
exponent(0),
fraction(0),
NaN_flag(false),
infinity_flag(false)
{
}

explicit ieee_floatt(const floatbv_typet &type):
rounding_mode(ROUND_TO_EVEN),
spec(ieee_float_spect(type)),
Expand Down