Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
f99f39c
Add native Mathsat test for mantissa not including the sign bit with …
Sep 1, 2025
737718c
Add note about mantissa und sign bit in FloatingPointNumber.java
Sep 1, 2025
d50ad4c
Add JavaDoc to FormulaType.FloatingPointType and add methods to get t…
Sep 1, 2025
a4240e6
Add JavaDoc to FloatingPointNumber and add methods to get the mantiss…
Sep 1, 2025
c452a8f
Extend FP tests with new mantissa API to be unambiguous about the sig…
Sep 1, 2025
a6f92b7
Update Bitwuzla with new unambiguous FP mantissa size getters
Sep 1, 2025
ca4a3f7
Update CVC4 with new unambiguous FP mantissa size getters
Sep 1, 2025
7bed3f5
Update CVC5 with new unambiguous FP mantissa size getters
Sep 1, 2025
73c1451
Update MathSAT5 with new unambiguous FP mantissa size getters
Sep 1, 2025
1ca40f9
Update Z3 with new unambiguous FP mantissa size getters
Sep 1, 2025
6d80321
Update SolverVisitorTest with new unambiguous FP mantissa size getters
Sep 1, 2025
44310e0
Fix off-by-one FP mantissa bug when casting BV to FP
Sep 2, 2025
475ce68
Remove changes left over from PR 512 (to be added back with PR 512!)
Sep 2, 2025
bb3760d
Fix bug in test using the wrong mantissa size for an FP
Sep 2, 2025
31b0b38
Apply checkstyle naming/grammar suggestions
Sep 2, 2025
4b07b9b
Fix off-by-one error for mantissa for getFloatingPointTypeWithSignBit()
Sep 2, 2025
e444b52
Update naming of mantissa arguments in constructors for FloatingPoint…
Sep 2, 2025
f1ff09f
Fix accidentally added off-by-one bug in CVC4 when building FPs
Sep 2, 2025
eddaf77
Use new FP mantissa getter in CVC4 and remove magic -1 + improve var…
Sep 2, 2025
1dae5d1
Unify constructors of FloatingPointType by delegating to the same met…
Sep 2, 2025
7755fe6
Use the SMTLIB2 standards interpretation of including the sign bit in…
Sep 2, 2025
83cd136
Add method for total type size in FloatingPointNumber and use it inst…
Sep 2, 2025
96a2565
Rename method for total type size in FloatingPointNumber to getTotalS…
Sep 2, 2025
885bb68
Add deprecation of both getMantissaSize() methods with reasoning
Sep 2, 2025
59713fe
Revert CVC4 fromIeeeBitvectorImpl() implementation and use new API ca…
Sep 3, 2025
05b6c40
Revert AbstractFloatingPointFormulaManager toIeeeBitvectorImpl() to p…
Sep 3, 2025
a08a811
Revert CVC4 toIeeeBitvectorImpl() to previous implementation and fix …
Sep 3, 2025
a2a7810
Revert CVC5 toIeeeBitvectorImpl() and fromIeeeBitvectorImpl() to prev…
Sep 3, 2025
f37786e
Fix error in assertion in CVC5 fromIeeeBitvectorImpl()
Sep 3, 2025
0bd0aa7
Fix error in assertion in CVC4 fromIeeeBitvectorImpl()
Sep 3, 2025
7331cd1
Add method to check the availability of native FP to BV in tests
Sep 3, 2025
031a900
Fix CVC4/5 size assertion in fromIeeeBitvectorImpl() again :D
Sep 3, 2025
3d17811
Add FP tests for mantissa size with and without sign bit, as well as …
Sep 3, 2025
55089a0
Improve constructor naming for FormulaType.FloatingPointType to bette…
Sep 3, 2025
d836779
Add total size check to FP checks for single/double precision in Floa…
Sep 3, 2025
0885fd0
Improve JavaDoc of FloatingPointType constructors
Sep 3, 2025
23cdc67
Update variable/const names of mantissas in FloatingPointNumber/Float…
Sep 3, 2025
d67273c
Improve CVC5 documentation in regard to mantissa including the sign b…
Sep 3, 2025
b7b8aff
Update code with "getMantissaSizeWithSignBit() - 1" in MathSAT5 to us…
Sep 3, 2025
225ba7a
Add info about the mantissa size to MathSAT5 native API
Sep 3, 2025
9df8b66
Add inlineMe annotations with replacements to newly deprecated FP met…
Sep 3, 2025
7207b71
Update more deprecated FP API with the new alternatives and simplify …
Sep 3, 2025
5086ae7
Update one more FP type constructor in CVC5 to better include the man…
Sep 3, 2025
6cb15ea
Update a mantissa size variable name to include sign bit in CVC4Float…
Sep 3, 2025
f760eb1
Update a mantissa size variable names to include sign bit in Bitwuzla…
Sep 3, 2025
f7c5916
Update an FP type constructor in Z3 to a better one
Sep 3, 2025
fbea69f
Simplify calculation of total size of FP type
Sep 5, 2025
e043988
Refactor some FP/precision sizes/mantissa code in CVC4
Sep 5, 2025
d9d2b3e
Remove accidentally added class
Sep 5, 2025
1aba81d
Use the correct getter for mantissas in Z3FormulaCreator for FPs
Sep 5, 2025
dd83ed4
Split BV to FP to BV tests and remove solverSupportsNativeFPToBitvect…
Sep 7, 2025
c83f96f
Update all names referencing the hidden bit in Floating-Point precisi…
Sep 7, 2025
e8f44d8
Revert some changes back to sign bit that were accidentally changed
Sep 7, 2025
00dc033
Improve some JavaDoc in FloatingPointFormulaManager.java in relation …
Sep 7, 2025
20a8f48
Add tests for FP type (precision) toString(), fromString(), and toSMT…
Sep 11, 2025
6769850
Remove @InlineMe annotation for deprecated call getMantissaSize(), as…
Sep 12, 2025
015c145
Remove @InlineMe annotation for deprecated call getMantissaSize(), as…
Sep 12, 2025
9aff78a
Disable parts of FP to IEEE BV tests for Z3, as it returns a query in…
Sep 12, 2025
ddac078
Enable FP to IEEE BV precision tests for Z3 and Bitwuzla fully by usi…
Sep 12, 2025
52d6e30
Re-add removed FP single/double mantissa size constants and deprecate…
Sep 12, 2025
5dd10ca
Add FP single/double precision sizes to FloatingPointNumberTest.java,…
Sep 12, 2025
e08a75a
Fix incorrect JavaDoc for FP precision total size API and make the ca…
Sep 12, 2025
bd8c6bd
Explicitly name sign bit in FloatingPointNumber constructor JavaDoc, …
baierd Sep 22, 2025
55901fc
Remove @InlineMe from deprecated method getFloatingPointType() so tha…
Sep 22, 2025
b835125
Rename wrongly named variable in Mathsat5 FP impl
Sep 22, 2025
056ca12
Rename wrongly named variable in CVC4 FP impl
Sep 22, 2025
234452d
Change comment about total FP precision size in fromIeeeBitvectorImpl…
Sep 22, 2025
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
11 changes: 6 additions & 5 deletions src/org/sosy_lab/java_smt/api/FloatingPointFormulaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

package org.sosy_lab.java_smt.api;

import static org.sosy_lab.java_smt.api.FormulaType.getFloatingPointType;
import static org.sosy_lab.java_smt.api.FormulaType.getFloatingPointTypeFromSizesWithoutHiddenBit;

import java.math.BigDecimal;
import java.math.BigInteger;
Expand Down Expand Up @@ -99,7 +99,8 @@ default FloatingPointFormula makeNumber(FloatingPointNumber number) {
number.getExponent(),
number.getMantissa(),
number.getMathSign(),
getFloatingPointType(number.getExponentSize(), number.getMantissaSize()));
getFloatingPointTypeFromSizesWithoutHiddenBit(
number.getExponentSize(), number.getMantissaSizeWithoutHiddenBit()));
}

/**
Expand Down Expand Up @@ -274,8 +275,8 @@ FloatingPointFormula castFrom(

/**
* Create a formula that interprets the given bitvector as a floating-point value in the IEEE
* format, according to the given type. The sum of the sizes of exponent and mantissa of the
* target type plus 1 (for the sign bit) needs to be equal to the size of the bitvector.
* format, according to the given type. The sum of the sizes of exponent and mantissa (including
* the hidden bit) of the target type needs to be equal to the size of the bitvector.
Comment on lines +278 to +279
Copy link
Member

Choose a reason for hiding this comment

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

This is wrong, right? It needs to refer to the sign bit, but exclude the hidden bit.

*
* <p>Note: This method will return a value that is (numerically) far away from the original
* value. This method is completely different from {@link #castFrom}, which will produce a
Expand All @@ -286,7 +287,7 @@ FloatingPointFormula castFrom(
/**
* Create a formula that produces a representation of the given floating-point value as a
* bitvector conforming to the IEEE format. The size of the resulting bitvector is the sum of the
* sizes of the exponent and mantissa of the input formula plus 1 (for the sign bit).
* sizes of the exponent and mantissa (including the hidden bit) of the input formula.
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

*/
BitvectorFormula toIeeeBitvector(FloatingPointFormula number);

Expand Down
135 changes: 107 additions & 28 deletions src/org/sosy_lab/java_smt/api/FloatingPointNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,31 @@
@AutoValue
public abstract class FloatingPointNumber {

// TODO: remove deprecated constants from public API after 6.0 release (and delete the unused).
@Deprecated(since = "6.0", forRemoval = true)
public static final int SINGLE_PRECISION_EXPONENT_SIZE = 8;

/**
* @deprecated this constant can be confusing, as the SMTLIB2 standard expects the mantissa to
* include the hidden bit, but this constant does not.
*/
@Deprecated(since = "6.0", forRemoval = true)
public static final int SINGLE_PRECISION_MANTISSA_SIZE = 23;

protected static final int SINGLE_PRECISION_MANTISSA_SIZE_WITHOUT_HIDDEN_BIT = 23;

@Deprecated(since = "6.0", forRemoval = true)
public static final int DOUBLE_PRECISION_EXPONENT_SIZE = 11;

/**
* @deprecated this constant can be confusing, as the SMTLIB2 standard expects the mantissa to
* include the hidden bit, but this constant does not.
*/
@Deprecated(since = "6.0", forRemoval = true)
public static final int DOUBLE_PRECISION_MANTISSA_SIZE = 52;

protected static final int DOUBLE_PRECISION_MANTISSA_SIZE_WITHOUT_HIDDEN_BIT = 52;

public enum Sign {
POSITIVE,
NEGATIVE;
Expand Down Expand Up @@ -80,7 +100,42 @@ public final boolean getSign() {

public abstract int getExponentSize();

public abstract int getMantissaSize();
/**
* Returns the size of the mantissa (also called a coefficient or significant), excluding the sign
* bit.
*
* @deprecated this method can be confusing, as the SMTLIB2 standard expects the mantissa to
* include the hidden bit, but this does not. Use {@link #getMantissaSizeWithoutHiddenBit()}
* instead if you want the mantissa without the hidden bit, and {@link
* #getMantissaSizeWithHiddenBit()} if you want it to include the hidden bit.
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("InlineMeSuggester")
public final int getMantissaSize() {
return getMantissaSizeWithoutHiddenBit();
}
Copy link
Member

Choose a reason for hiding this comment

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

Can we not make this method deprecated and just document that it returns the mathematical mantissa size, not the SMTLIB-based size? That would reduce this API change and internal changes quite well.

Copy link
Member

Choose a reason for hiding this comment

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

If there is an operation "foo" with two variants "A" and "B", of which both are common and correct, but neither is clearly the default or more general, then the respective methods should be named "fooA" and "fooB" and not "foo" and "fooB". Otherwise a lot of developers that need "fooB" will just call "foo" accidentally, even if the JavaDoc explains that it does "fooA". Here, getMantissaSizeWithoutSignBit() is not clearly the default or more general, in particular given the fact that SMTLIB handles it differently.

This is especially true if many developers are not even aware that there are two variants of the operation, which is certainly also the case here. For such a developer a method call like getMantissaSize() will look obviously correct if they need the mantissa size, and they will certainly not bother with reading the JavaDoc (nobody reads the JavaDoc of every single trivial getter they call) or looking for alternatives if auto-complete offers getMantissaSize(). So they will call it even if they need the other method. Thus both method names need to raise awareness that there are two variants.

Another argument is when code is read, especially outside of an IDE, for example while reviewing something. If you see a diff with a call to getMantissaSize(), you need to be highly familiar with fp encodings in order to even notice that this could be ambiguous and then got out of your way to lookup the JavaDoc somewhere else to find out the exact semantics of getMantissaSize() because there is no way to quickly access it from where you are currently.

I would, however, say that the old name does not need to be marked for removal. It could stay deprecated indefinitely.

And I would say that @InlineMe should not be added. This annotation will allow users of Error-Prone to automatically apply an inlining without having to look at it manually. But this will simply change their code to call getMantissaSizeWithoutSignBit(), even if there is actually a bug in their code and they would need getMantissaSizeWithSignBit(). So I think it is better to mark the method only as deprecated. This will produce warnings and when they see a deprecation warning developers look at the JavaDoc of the deprecated method to understand it, so we can explain there why it is deprecated and that callers need to find out which of the two new methods they need. @InlineMe is for refactorings that are clearly an improvement.


/**
* Returns the size of the mantissa (also called a coefficient or significant), excluding the
* hidden bit.
*/
public abstract int getMantissaSizeWithoutHiddenBit();

/**
* Returns the size of the mantissa (also called a coefficient or significant), including the
* hidden bit.
*/
public int getMantissaSizeWithHiddenBit() {
return getMantissaSizeWithoutHiddenBit() + 1;
}

/**
* Returns the size of the precision, i.e. the single sign bit + the size of the exponent + the
* size of the mantissa (excluding the hidden bit).
*/
public int getTotalSize() {
return getMantissaSizeWithoutHiddenBit() + getExponentSize() + 1;
}

/**
* Get a floating-point number with the given sign, exponent, and mantissa.
Expand All @@ -92,22 +147,28 @@ public final boolean getSign() {
* @param mantissa the mantissa of the floating-point number, given as unsigned (not negative)
* number without hidden bit
* @param exponentSize the (maximum) size of the exponent in bits
* @param mantissaSize the (maximum) size of the mantissa in bits
* @param mantissaSizeWithoutHiddenBit the (maximum) size of the mantissa in bits (excluding the
* hidden bit)
* @see #of(Sign, BigInteger, BigInteger, int, int)
*/
@Deprecated(
since = "2025.01, because using a boolean flag as signBit is misleading",
forRemoval = true)
@InlineMe(
replacement =
"FloatingPointNumber.of(Sign.of(sign), exponent, mantissa, exponentSize, mantissaSize)",
"FloatingPointNumber.of(Sign.of(sign), exponent, mantissa, exponentSize,"
+ " mantissaSizeWithoutHiddenBit)",
imports = {
"org.sosy_lab.java_smt.api.FloatingPointNumber",
"org.sosy_lab.java_smt.api.FloatingPointNumber.Sign"
})
public static FloatingPointNumber of(
boolean sign, BigInteger exponent, BigInteger mantissa, int exponentSize, int mantissaSize) {
return of(Sign.of(sign), exponent, mantissa, exponentSize, mantissaSize);
boolean sign,
BigInteger exponent,
BigInteger mantissa,
int exponentSize,
int mantissaSizeWithoutHiddenBit) {
return of(Sign.of(sign), exponent, mantissa, exponentSize, mantissaSizeWithoutHiddenBit);
}

/**
Expand All @@ -119,15 +180,21 @@ public static FloatingPointNumber of(
* @param mantissa the mantissa of the floating-point number, given as unsigned (not negative)
* number without hidden bit
* @param exponentSize the (maximum) size of the exponent in bits
* @param mantissaSize the (maximum) size of the mantissa in bits
* @param mantissaSizeWithoutHiddenBit the (maximum) size of the mantissa in bits (excluding the
* hidden bit)
*/
public static FloatingPointNumber of(
Sign sign, BigInteger exponent, BigInteger mantissa, int exponentSize, int mantissaSize) {
Sign sign,
BigInteger exponent,
BigInteger mantissa,
int exponentSize,
int mantissaSizeWithoutHiddenBit) {
Preconditions.checkArgument(exponent.bitLength() <= exponentSize);
Preconditions.checkArgument(mantissa.bitLength() <= mantissaSize);
Preconditions.checkArgument(mantissa.bitLength() <= mantissaSizeWithoutHiddenBit);
Preconditions.checkArgument(exponent.compareTo(BigInteger.ZERO) >= 0);
Preconditions.checkArgument(mantissa.compareTo(BigInteger.ZERO) >= 0);
return new AutoValue_FloatingPointNumber(sign, exponent, mantissa, exponentSize, mantissaSize);
return new AutoValue_FloatingPointNumber(
sign, exponent, mantissa, exponentSize, mantissaSizeWithoutHiddenBit);
}

/**
Expand All @@ -136,47 +203,59 @@ public static FloatingPointNumber of(
* @param bits the bit-representation of the floating-point number, consisting of sign bit,
* exponent (without bias) and mantissa (without hidden bit) in this exact ordering
* @param exponentSize the size of the exponent in bits
* @param mantissaSize the size of the mantissa in bits
* @param mantissaSizeWithoutHiddenBit the size of the mantissa in bits (excluding the hidden bit)
*/
public static FloatingPointNumber of(String bits, int exponentSize, int mantissaSize) {
public static FloatingPointNumber of(
String bits, int exponentSize, int mantissaSizeWithoutHiddenBit) {
Preconditions.checkArgument(0 < exponentSize);
Preconditions.checkArgument(0 < mantissaSize);
Preconditions.checkArgument(0 < mantissaSizeWithoutHiddenBit);
Preconditions.checkArgument(
bits.length() == 1 + exponentSize + mantissaSize,
bits.length() == 1 + exponentSize + mantissaSizeWithoutHiddenBit,
"Bitsize (%s) of floating point numeral does not match the size of sign, exponent and "
+ "mantissa (%s + %s + %s).",
bits.length(),
1,
exponentSize,
mantissaSize);
mantissaSizeWithoutHiddenBit);
Preconditions.checkArgument(bits.chars().allMatch(c -> c == '0' || c == '1'));
Sign sign = Sign.of(bits.charAt(0) == '1');
BigInteger exponent = new BigInteger(bits.substring(1, 1 + exponentSize), 2);
BigInteger mantissa =
new BigInteger(bits.substring(1 + exponentSize, 1 + exponentSize + mantissaSize), 2);
return of(sign, exponent, mantissa, exponentSize, mantissaSize);
new BigInteger(
bits.substring(1 + exponentSize, 1 + exponentSize + mantissaSizeWithoutHiddenBit), 2);
return of(sign, exponent, mantissa, exponentSize, mantissaSizeWithoutHiddenBit);
}

/**
* Returns true if this floating-point number is an IEEE-754-2008 single precision type with 32
* bits length consisting of an 8 bit exponent, a 23 bit mantissa and a single sign bit.
* bits length consisting of an 8 bit exponent, a 24 bit mantissa (including the hidden bit).
Copy link
Member

Choose a reason for hiding this comment

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

The sentence is grammatically wrong now, and it misses the sign bit.

*
* @return true for IEEE-754 single precision type, false otherwise.
*/
public boolean isIEEE754SinglePrecision() {
return getExponentSize() == SINGLE_PRECISION_EXPONENT_SIZE
&& getMantissaSize() == SINGLE_PRECISION_MANTISSA_SIZE;
// Mantissa does not include the hidden bit internally
return getTotalSize()
== SINGLE_PRECISION_EXPONENT_SIZE
+ SINGLE_PRECISION_MANTISSA_SIZE_WITHOUT_HIDDEN_BIT
+ 1
&& getExponentSize() == SINGLE_PRECISION_EXPONENT_SIZE
&& getMantissaSizeWithoutHiddenBit() == SINGLE_PRECISION_MANTISSA_SIZE_WITHOUT_HIDDEN_BIT;
}

/**
* Returns true if this floating-point number is an IEEE-754-2008 double precision type with 64
* bits length consisting of an 11 bit exponent, a 52 bit mantissa and a single sign bit.
* bits length consisting of an 11 bit exponent, a 53 bit mantissa (including the hidden bit).
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

*
* @return true for IEEE-754 double precision type, false otherwise.
*/
public boolean isIEEE754DoublePrecision() {
return getExponentSize() == DOUBLE_PRECISION_EXPONENT_SIZE
&& getMantissaSize() == DOUBLE_PRECISION_MANTISSA_SIZE;
// Mantissa does not include the hidden bit internally
return getTotalSize()
== DOUBLE_PRECISION_EXPONENT_SIZE
+ DOUBLE_PRECISION_MANTISSA_SIZE_WITHOUT_HIDDEN_BIT
+ 1
&& getExponentSize() == DOUBLE_PRECISION_EXPONENT_SIZE
&& getMantissaSizeWithoutHiddenBit() == DOUBLE_PRECISION_MANTISSA_SIZE_WITHOUT_HIDDEN_BIT;
}

/** compute a representation as Java-based float value, if possible. */
Expand Down Expand Up @@ -204,18 +283,18 @@ public double doubleValue() {
}

private BitSet getBits() {
var mantissaSize = getMantissaSize();
var mantissaSizeWithoutSign = getMantissaSizeWithoutHiddenBit();
var exponentSize = getExponentSize();
var mantissa = getMantissa();
var exponent = getExponent();
var bits = new BitSet(1 + exponentSize + mantissaSize);
var bits = new BitSet(getTotalSize());
if (getMathSign().isNegative()) {
bits.set(exponentSize + mantissaSize); // if negative, set first bit to 1
bits.set(exponentSize + mantissaSizeWithoutSign); // if negative, set first bit to 1
}
for (int i = 0; i < exponentSize; i++) {
bits.set(mantissaSize + i, exponent.testBit(i));
bits.set(mantissaSizeWithoutSign + i, exponent.testBit(i));
}
for (int i = 0; i < mantissaSize; i++) {
for (int i = 0; i < mantissaSizeWithoutSign; i++) {
bits.set(i, mantissa.testBit(i));
}
return bits;
Expand All @@ -227,7 +306,7 @@ private BitSet getBits() {
*/
@Override
public final String toString() {
var length = 1 + getExponentSize() + getMantissaSize();
var length = getTotalSize();
var str = new StringBuilder(length);
var bits = getBits();
for (int i = 0; i < length; i++) {
Expand Down
Loading