Theory of Floating-Points

examples/api/cpp/floating_point_arith.cpp

  1/******************************************************************************
  2 * Top contributors (to current version):
  3 *   Aina Niemetz, Mudathir Mohamed, Andres Noetzli
  4 *
  5 * This file is part of the cvc5 project.
  6 *
  7 * Copyright (c) 2009-2024 by the authors listed in the file AUTHORS
  8 * in the top-level source directory and their institutional affiliations.
  9 * All rights reserved.  See the file COPYING in the top-level source
 10 * directory for licensing information.
 11 * ****************************************************************************
 12 *
 13 * An example of solving floating-point problems with cvc5's cpp API.
 14 *
 15 * This example shows to create floating-point types, variables and expressions,
 16 * and how to create rounding mode constants by solving toy problems. The
 17 * example also shows making special values (such as NaN and +oo) and converting
 18 * an IEEE 754-2008 bit-vector to a floating-point number.
 19 */
 20
 21#include <cvc5/cvc5.h>
 22
 23#include <iostream>
 24#include <cassert>
 25
 26using namespace std;
 27using namespace cvc5;
 28
 29int main()
 30{
 31  TermManager tm;
 32  Solver solver(tm);
 33  solver.setOption("produce-models", "true");
 34
 35  // Make single precision floating-point variables
 36  Sort fpt32 = tm.mkFloatingPointSort(8, 24);
 37  Term a = tm.mkConst(fpt32, "a");
 38  Term b = tm.mkConst(fpt32, "b");
 39  Term c = tm.mkConst(fpt32, "c");
 40  Term d = tm.mkConst(fpt32, "d");
 41  Term e = tm.mkConst(fpt32, "e");
 42
 43  // Assert that floating-point addition is not associative:
 44  // (a + (b + c)) != ((a + b) + c)
 45  Term rm = tm.mkRoundingMode(RoundingMode::ROUND_NEAREST_TIES_TO_EVEN);
 46  Term lhs = tm.mkTerm(Kind::FLOATINGPOINT_ADD,
 47                       {rm, a, tm.mkTerm(Kind::FLOATINGPOINT_ADD, {rm, b, c})});
 48  Term rhs = tm.mkTerm(Kind::FLOATINGPOINT_ADD,
 49                       {rm, tm.mkTerm(Kind::FLOATINGPOINT_ADD, {rm, a, b}), c});
 50  solver.assertFormula(tm.mkTerm(Kind::NOT, {tm.mkTerm(Kind::EQUAL, {a, b})}));
 51
 52  Result r = solver.checkSat();  // result is sat
 53  assert (r.isSat());
 54
 55  cout << "a = " << solver.getValue(a) << endl;
 56  cout << "b = " << solver.getValue(b) << endl;
 57  cout << "c = " << solver.getValue(c) << endl;
 58
 59  // Now, let's restrict `a` to be either NaN or positive infinity
 60  Term nan = tm.mkFloatingPointNaN(8, 24);
 61  Term inf = tm.mkFloatingPointPosInf(8, 24);
 62  solver.assertFormula(tm.mkTerm(
 63      Kind::OR,
 64      {tm.mkTerm(Kind::EQUAL, {a, inf}), tm.mkTerm(Kind::EQUAL, {a, nan})}));
 65
 66  r = solver.checkSat();  // result is sat
 67  assert (r.isSat());
 68
 69  cout << "a = " << solver.getValue(a) << endl;
 70  cout << "b = " << solver.getValue(b) << endl;
 71  cout << "c = " << solver.getValue(c) << endl;
 72
 73  // And now for something completely different. Let's try to find a (normal)
 74  // floating-point number that rounds to different integer values for
 75  // different rounding modes.
 76  Term rtp = tm.mkRoundingMode(RoundingMode::ROUND_TOWARD_POSITIVE);
 77  Term rtn = tm.mkRoundingMode(RoundingMode::ROUND_TOWARD_NEGATIVE);
 78  Op op = tm.mkOp(Kind::FLOATINGPOINT_TO_SBV, {16});  // (_ fp.to_sbv 16)
 79  lhs = tm.mkTerm(op, {rtp, d});
 80  rhs = tm.mkTerm(op, {rtn, d});
 81  solver.assertFormula(tm.mkTerm(Kind::FLOATINGPOINT_IS_NORMAL, {d}));
 82  solver.assertFormula(
 83      tm.mkTerm(Kind::NOT, {tm.mkTerm(Kind::EQUAL, {lhs, rhs})}));
 84
 85  r = solver.checkSat();  // result is sat
 86  assert (r.isSat());
 87
 88  // Convert the result to a rational and print it
 89  Term val = solver.getValue(d);
 90  Term realVal = solver.getValue(tm.mkTerm(Kind::FLOATINGPOINT_TO_REAL, {val}));
 91  cout << "d = " << val << " = " << realVal << endl;
 92  cout << "((_ fp.to_sbv 16) RTP d) = " << solver.getValue(lhs) << endl;
 93  cout << "((_ fp.to_sbv 16) RTN d) = " << solver.getValue(rhs) << endl;
 94
 95  // For our final trick, let's try to find a floating-point number between
 96  // positive zero and the smallest positive floating-point number
 97  Term zero = tm.mkFloatingPointPosZero(8, 24);
 98  Term smallest = tm.mkFloatingPoint(8, 24, tm.mkBitVector(32, 0b001));
 99  solver.assertFormula(
100      tm.mkTerm(Kind::AND,
101                {tm.mkTerm(Kind::FLOATINGPOINT_LT, {zero, e}),
102                 tm.mkTerm(Kind::FLOATINGPOINT_LT, {e, smallest})}));
103
104  r = solver.checkSat();  // result is unsat
105  assert (!r.isSat());
106}