Skip to content

Conversation

@schuessf
Copy link
Contributor

PR #714 refactored the way standard functions in C are modeled in Boogie. As a result, separate classes are now used to handle functions from specific C libraries. In particular, the class MathLibraryModel is responsible for modeling functions from math.h. However, in case of MathLibraryModel the actual translation is delegated to BitvectorTranslation::constructOtherUnaryFloatOperation and BitvectorTranslation::constructOtherBinaryFloatOperation (the other implementations of those methods in IntegerTranslation simply throw an exception, since these float operations are not supported), while FloatFunction serves as some kind of connection (with some hacky string operations to determine the C type). Consequently, whenever support for new math library functions is added, three classes had to be changed (MathLibraryModel, BitvectorTranslation, FloatFunction), which made the process cumbersome.

Therefore, this PR removed all the handling of standard functions from BitvectorTranslation, as well as the (rather hacky) class FloatFunction and moved the translation of functions from math.h entirely to MathLibraryModel (similar as done in other library models). To facilitate this, new methods were added to ExpressionTranslation to serve as a proper API. In BitvectorTranslation these methods implement calls to SMT-defined floating-point theory functions, while in IntegerTranslation, they throw exceptions.

Additionally, this PR added support for more functions from math.h (with the help of overapproximations):

  • signbit, remainder, copysign: They were removed in 042d866 (see this comment), and since then only an exception was thrown. This was due to an unsoundness, since we were not able to handle negative NaNs in SMT, but they can occur in C (with the help of copysign). While some efforts were made to handle these cases precisely (see Represent ambivalent floats as bitvectors #440), they were left incomplete, so this PR uses overapproximations instead. Specifically, when NaN values appear as arguments to copysign or signbit, the result is safely overapproximated, which remains precise enough for most of the cases.
  • Several numeric functions (e.g., sin, exp, ...) were added, with an error handling (i.e., ±0, ±∞ and NaN as arguments) according to the C standard. For all other return values, a very coarse overapproximation is used, capturing simple properties - for example sin(x) is always between -1 and 1, and exp(x) is always at least x+1. Despite this coarse overapproximation, the translation should still be precise enough to verify some (very simple) tasks of the neural-networks benchmarks.

schuessf added 30 commits August 6, 2025 16:36
("The CNF conversion does not support quantifiers")
@schuessf schuessf added feature C translation float Things that originate from the treatment of IEEE 754 floats labels Sep 29, 2025
@danieldietsch
Copy link
Member

@schuessf @bahnwaerter does this lead to conflicts with the refactored memory model?

@schuessf
Copy link
Contributor Author

@schuessf @bahnwaerter does this lead to conflicts with the refactored memory model?

I don't think so, this PR basically just changes MathLibraryModel and removes two methods from BitvectorTranslation, whereas the memory model refactoring mostly changes MemoryHandler.

# Conflicts:
#	trunk/source/CACSL2BoogieTranslator/src/de/uni_freiburg/informatik/ultimate/cdt/translation/implementation/base/expressiontranslation/BitvectorTranslation.java
Copy link
Contributor

@maul-esel maul-esel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks for making this effort to further improve our library handling, and even extending it!

I don't know much about floating-point arithmetic, so I couldn't review those aspects in detail. I have left some comments/questions mostly on the architecture.

}

private ExpressionResult handleSqrt(final IDispatcher main, final IASTFunctionCallExpression node,
final ILocation loc, final String name, final CPrimitive type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor refactoring suggestion: why not let these kinds of methods take a CPrimitives and have one new CPrimitive(...) call in the method, rather than one in every usage? This will improve readability of getFunctionModels.

Copy link
Contributor

@maul-esel maul-esel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple more small comments.

}

@Override
public Expression getCurrentRoundingMode() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another candidate that could potentially be moved to IFloatingPointHandler

@bahnwaerter
Copy link
Member

@schuessf @bahnwaerter does this lead to conflicts with the refactored memory model?

I don't think so, this PR basically just changes MathLibraryModel and removes two methods from BitvectorTranslation, whereas the memory model refactoring mostly changes MemoryHandler.

You're right. The changes only affect the memory model, which particularly affects the memory structure and addressing. There may be a small merge conflict, but this should be very easy to resolve. I would suggest that we merge the memory model first (as soon as the last unsoundness issues have been resolved) and then merge this pull request.

// signbit(x) := isNegative(x) ? 1 : 0;
new OverapproxVariable("sign of NaN", loc).annotate(nondet);
final Expression zero = mExpressionTranslation.constructLiteralForIntegerType(loc, intType, BigInteger.ZERO);
builder.addStatement(StatementFactory.constructIfStatement(loc, mFloatHandler.isNan(loc, argument, type),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danieldietsch @Heizmann As you have worked with floats in the past, does this handling of signbit (incl. the overapproximation) seem reasonable?

type)));
// TODO: Overapproximate if second is NaN
new OverapproxVariable("sign of NaN", loc).annotate(nondet);
builder.addStatement(StatementFactory.constructIfStatement(loc, mFloatHandler.isNan(loc, first, type),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danieldietsch @Heizmann As you have worked with floats in the past, does this handling of copysign (incl. the overapproximation) seem reasonable?

final List<ExpressionResult> arguments = handleFloatArguments(main, node, loc, name, 2, type);
final Expression first = arguments.get(0).getLrValue().getValue();
final Expression second = arguments.get(1).getLrValue().getValue();
return new ExpressionResultBuilder().addAllExceptLrValue(arguments)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danieldietsch In 042d866 you removed also the support for remainder, claiming in this comment that our handling was also unsound. In this PR, however, I added support again using the SMT function fp.rem. The documentations for fp.rem and remainder seemed to match and I could not find any unsoundness. Do you remember the issue with remainder?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C translation feature float Things that originate from the treatment of IEEE 754 floats

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants