Skip to content

Commit 693da11

Browse files
authored
Merge pull request #440 from sosy-lab/feat/improve-fp-documentation
FP theory: improve documentation and tests.
2 parents 11dbd96 + 26c14d0 commit 693da11

File tree

3 files changed

+293
-25
lines changed

3 files changed

+293
-25
lines changed

src/org/sosy_lab/java_smt/api/FloatingPointFormulaManager.java

+95-5
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,16 @@ FloatingPointFormula castFrom(
298298

299299
FloatingPointFormula abs(FloatingPointFormula number);
300300

301+
/**
302+
* Returns the maximum value of the two given floating-point numbers. If one of the numbers is
303+
* NaN, the other number is returned.
304+
*/
301305
FloatingPointFormula max(FloatingPointFormula number1, FloatingPointFormula number2);
302306

307+
/**
308+
* Returns the minimum value of the two given floating-point numbers. If one of the numbers is
309+
* NaN, the other number is returned.
310+
*/
303311
FloatingPointFormula min(FloatingPointFormula number1, FloatingPointFormula number2);
304312

305313
FloatingPointFormula sqrt(FloatingPointFormula number);
@@ -345,36 +353,118 @@ FloatingPointFormula multiply(
345353

346354
/**
347355
* Create a term for assigning one floating-point term to another. This means both terms are
348-
* considered equal afterwards. This method is the same as the method <code>equal</code> for other
349-
* theories.
356+
* considered equal afterward, based on their bit pattern (i.e., <code>0.0 != -0
357+
* .0</code> and <code>NaN ==/!= NaN</code>, depending on the bit pattern of each NaN). This
358+
* method is the same as the method <code>equal</code> for other theories.
350359
*/
351360
BooleanFormula assignment(FloatingPointFormula number1, FloatingPointFormula number2);
352361

353362
/**
354363
* Create a term for comparing the equality of two floating-point terms, according to standard
355-
* floating-point semantics (i.e., NaN != NaN). Be careful to not use this method when you really
356-
* need {@link #assignment(FloatingPointFormula, FloatingPointFormula)}.
364+
* floating-point semantics (i.e., <code>NaN != NaN</code> and <code>0.0 == -0.0</code>). Be
365+
* careful to not use this method when you really need {@link #assignment(FloatingPointFormula,
366+
* FloatingPointFormula)}.
357367
*/
358368
BooleanFormula equalWithFPSemantics(FloatingPointFormula number1, FloatingPointFormula number2);
359369

370+
/**
371+
* Returns whether an FP number is greater than another FP number. If one of the numbers is NaN,
372+
* the result is always false.
373+
*/
360374
BooleanFormula greaterThan(FloatingPointFormula number1, FloatingPointFormula number2);
361375

376+
/**
377+
* Returns whether an FP number is greater or equal than another FP number. If one of the numbers
378+
* is NaN, the result is always false.
379+
*/
362380
BooleanFormula greaterOrEquals(FloatingPointFormula number1, FloatingPointFormula number2);
363381

382+
/**
383+
* Returns whether an FP number is less than another FP number. If one of the numbers is NaN, the
384+
* result is always false.
385+
*/
364386
BooleanFormula lessThan(FloatingPointFormula number1, FloatingPointFormula number2);
365387

388+
/**
389+
* Returns whether an FP number is less or equal than another FP number. If one of the numbers is
390+
* NaN, the result is always false.
391+
*/
366392
BooleanFormula lessOrEquals(FloatingPointFormula number1, FloatingPointFormula number2);
367393

394+
/**
395+
* Check whether a floating-point number is NaN.
396+
*
397+
* <p>The bit patterns for NaN in SMTLIB are identical to IEEE 754:
398+
*
399+
* <ul>
400+
* <li>sign=? (irrelevant for NaN)
401+
* <li>exponent=11...11 (all bits are 1)
402+
* <li>mantissa!=00...00 (mantissa is not all 0)
403+
* </ul>
404+
*/
368405
BooleanFormula isNaN(FloatingPointFormula number);
369406

407+
/**
408+
* Checks whether a formula is positive or negative infinity.
409+
*
410+
* <p>The bit patterns for infinity in SMTLIB are identical to IEEE 754:
411+
*
412+
* <ul>
413+
* <li>sign=? (0 for +Inf, 1 for -Inf)
414+
* <li>exponent=11...11 (all bits are 1)
415+
* <li>mantissa=00...00 (all bits are 0)
416+
* </ul>
417+
*/
370418
BooleanFormula isInfinity(FloatingPointFormula number);
371419

420+
/**
421+
* Checks whether a formula is positive or negative zero.
422+
*
423+
* <p>The bit patterns for zero in SMTLIB are identical to IEEE 754:
424+
*
425+
* <ul>
426+
* <li>sign=? (0 for +0, 1 for -0)
427+
* <li>exponent=00...00 (all bits are 0)
428+
* <li>mantissa=00...00 (all bits are 0)
429+
* </ul>
430+
*/
372431
BooleanFormula isZero(FloatingPointFormula number);
373432

433+
/**
434+
* Checks whether a formula is normal FP number.
435+
*
436+
* <p>The bit patterns for normal FP numbers in SMTLIB are identical to IEEE 754:
437+
*
438+
* <ul>
439+
* <li>sign=? (0 for positive numbers, 1 for negative numbers)
440+
* <li>exponent!=00...00 and exponent!=11...11 (exponent is not all 0 or all 1)
441+
* <li>mantissa=? (mantissa is irrelevant)
442+
* </ul>
443+
*/
374444
BooleanFormula isNormal(FloatingPointFormula number);
375445

446+
/**
447+
* Checks whether a formula is subnormal FP number.
448+
*
449+
* <p>The bit patterns for subnormal FP numbers in SMTLIB are identical to IEEE 754:
450+
*
451+
* <ul>
452+
* <li>sign=? (0 for positive numbers, 1 for negative numbers)
453+
* <li>exponent=00...00 (exponent is all 0)
454+
* <li>mantissa!=00...00 (mantissa is not all 0)
455+
* </ul>
456+
*/
376457
BooleanFormula isSubnormal(FloatingPointFormula number);
377458

378-
/** checks whether a formula is negative, including -0.0. */
459+
/**
460+
* Checks whether a formula is negative, including -0.0.
461+
*
462+
* <p>The bit patterns for negative FP numbers in SMTLIB are identical to IEEE 754:
463+
*
464+
* <ul>
465+
* <li>sign=1 (1 for negative numbers)
466+
* <li>number is not NaN, i.e., exponent=11...11 implies mantissa=00...00
467+
* </ul>
468+
*/
379469
BooleanFormula isNegative(FloatingPointFormula number);
380470
}

src/org/sosy_lab/java_smt/test/FloatingPointFormulaManagerTest.java

+184-20
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,25 @@ public void nanAssignedNanIsTrue() throws SolverException, InterruptedException
190190
assertEqualsAsFormula(nan, nan);
191191
}
192192

193+
@Test
194+
public void nanOrdering() throws SolverException, InterruptedException {
195+
for (FloatingPointFormula other : new FloatingPointFormula[] {zero, posInf, negInf}) {
196+
assertThatFormula(fpmgr.greaterThan(nan, other)).isUnsatisfiable();
197+
assertThatFormula(fpmgr.greaterOrEquals(nan, other)).isUnsatisfiable();
198+
assertThatFormula(fpmgr.lessThan(nan, other)).isUnsatisfiable();
199+
assertThatFormula(fpmgr.lessOrEquals(nan, other)).isUnsatisfiable();
200+
assertEqualsAsFormula(fpmgr.max(nan, other), other);
201+
assertEqualsAsFormula(fpmgr.min(nan, other), other);
202+
203+
assertThatFormula(fpmgr.greaterThan(other, nan)).isUnsatisfiable();
204+
assertThatFormula(fpmgr.greaterOrEquals(other, nan)).isUnsatisfiable();
205+
assertThatFormula(fpmgr.lessThan(other, nan)).isUnsatisfiable();
206+
assertThatFormula(fpmgr.lessOrEquals(other, nan)).isUnsatisfiable();
207+
assertEqualsAsFormula(fpmgr.max(other, nan), other);
208+
assertEqualsAsFormula(fpmgr.min(other, nan), other);
209+
}
210+
}
211+
193212
@Test
194213
public void infinityOrdering() throws SolverException, InterruptedException {
195214
BooleanFormula order1 = fpmgr.greaterThan(posInf, zero);
@@ -320,6 +339,166 @@ public void specialValueFunctions() throws SolverException, InterruptedException
320339
assertThatFormula(fpmgr.isZero(minPosNormalValue)).isUnsatisfiable();
321340
}
322341

342+
@Test
343+
public void specialValueFunctionsFrom32Bits() throws SolverException, InterruptedException {
344+
float posInfFromBits = Float.intBitsToFloat(0x7f80_0000);
345+
assertThatFormula(fpmgr.isInfinity(fpmgr.makeNumber(posInfFromBits, singlePrecType)))
346+
.isTautological();
347+
348+
float negInfFromBits = Float.intBitsToFloat(0xff80_0000);
349+
assertThatFormula(fpmgr.isInfinity(fpmgr.makeNumber(negInfFromBits, singlePrecType)))
350+
.isTautological();
351+
352+
float zeroFromBits = Float.intBitsToFloat(0x0000_0000);
353+
assertThatFormula(fpmgr.isZero(fpmgr.makeNumber(zeroFromBits, singlePrecType)))
354+
.isTautological();
355+
356+
float negZeroFromBits = Float.intBitsToFloat(0x8000_0000);
357+
assertThatFormula(fpmgr.isZero(fpmgr.makeNumber(negZeroFromBits, singlePrecType)))
358+
.isTautological();
359+
360+
for (float nanFromBits :
361+
new float[] {
362+
Float.intBitsToFloat(0x7fc0_0001),
363+
Float.intBitsToFloat(0x7fc0_0002),
364+
Float.intBitsToFloat(0x7fc0_0003),
365+
Float.intBitsToFloat(0x7fc1_2345),
366+
Float.intBitsToFloat(0x7fdf_5678),
367+
Float.intBitsToFloat(0x7ff0_0001),
368+
// there are some more combinations for NaN, too much for one small test.
369+
}) {
370+
assertThatFormula(fpmgr.isNaN(fpmgr.makeNumber(nanFromBits, singlePrecType)))
371+
.isTautological();
372+
}
373+
}
374+
375+
@Test
376+
public void specialValueFunctionsFrom64Bits() throws SolverException, InterruptedException {
377+
double posInfFromBits = Double.longBitsToDouble(0x7ff0_0000_0000_0000L);
378+
assertThatFormula(fpmgr.isInfinity(fpmgr.makeNumber(posInfFromBits, doublePrecType)))
379+
.isTautological();
380+
381+
double negInfFromBits = Double.longBitsToDouble(0xfff0_0000_0000_0000L);
382+
assertThatFormula(fpmgr.isInfinity(fpmgr.makeNumber(negInfFromBits, doublePrecType)))
383+
.isTautological();
384+
385+
double zeroFromBits = Double.longBitsToDouble(0x0000_0000_0000_0000L);
386+
assertThatFormula(fpmgr.isZero(fpmgr.makeNumber(zeroFromBits, doublePrecType)))
387+
.isTautological();
388+
389+
double negZeroFromBits = Double.longBitsToDouble(0x8000_0000_0000_0000L);
390+
assertThatFormula(fpmgr.isZero(fpmgr.makeNumber(negZeroFromBits, doublePrecType)))
391+
.isTautological();
392+
393+
for (double nanFromBits :
394+
new double[] {
395+
Double.longBitsToDouble(0x7ff8_0000_0000_0001L),
396+
Double.longBitsToDouble(0x7ff8_0000_0000_0002L),
397+
Double.longBitsToDouble(0x7ff8_0000_0000_0003L),
398+
Double.longBitsToDouble(0x7ff8_1234_5678_9abcL),
399+
Double.longBitsToDouble(0x7ffc_9876_5432_1001L),
400+
Double.longBitsToDouble(0x7fff_ffff_ffff_fff2L),
401+
// there are some more combinations for NaN, too much for one small test.
402+
}) {
403+
assertThatFormula(fpmgr.isNaN(fpmgr.makeNumber(nanFromBits, doublePrecType)))
404+
.isTautological();
405+
}
406+
}
407+
408+
@Test
409+
public void specialValueFunctionsFrom32Bits2() throws SolverException, InterruptedException {
410+
requireBitvectors();
411+
requireFPToBitvector();
412+
413+
final FloatingPointFormula x = fpmgr.makeVariable("x32", singlePrecType);
414+
final BitvectorFormula signBit = bvmgr.extract(fpmgr.toIeeeBitvector(x), 31, 31);
415+
final BitvectorFormula exponent = bvmgr.extract(fpmgr.toIeeeBitvector(x), 30, 23);
416+
final BitvectorFormula mantissa = bvmgr.extract(fpmgr.toIeeeBitvector(x), 22, 0);
417+
418+
assertThatFormula(fpmgr.isInfinity(x))
419+
.isEquivalentTo(
420+
bmgr.or(
421+
bvmgr.equal(fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(32, 0x7f80_0000L)),
422+
bvmgr.equal(fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(32, 0xff80_0000L))));
423+
424+
assertThatFormula(fpmgr.isZero(x))
425+
.isEquivalentTo(
426+
bmgr.or(
427+
bvmgr.equal(fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(32, 0x0000_0000)),
428+
bvmgr.equal(fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(32, 0x8000_0000L))));
429+
430+
assertThatFormula(fpmgr.isNormal(x))
431+
.isEquivalentTo(
432+
bmgr.and(
433+
bmgr.not(bvmgr.equal(exponent, bvmgr.makeBitvector(8, 0))),
434+
bmgr.not(bvmgr.equal(exponent, bvmgr.makeBitvector(8, -1)))));
435+
436+
assertThatFormula(fpmgr.isSubnormal(x))
437+
.isEquivalentTo(
438+
bmgr.and(
439+
bvmgr.equal(exponent, bvmgr.makeBitvector(8, 0)),
440+
bmgr.not(bvmgr.equal(mantissa, bvmgr.makeBitvector(23, 0)))));
441+
442+
assertThatFormula(fpmgr.isNaN(x))
443+
.isEquivalentTo(
444+
bmgr.and(
445+
bvmgr.equal(exponent, bvmgr.makeBitvector(8, -1)),
446+
bmgr.not(bvmgr.equal(mantissa, bvmgr.makeBitvector(23, 0)))));
447+
448+
assertThatFormula(fpmgr.isNegative(x))
449+
.isEquivalentTo(
450+
bmgr.and(bmgr.not(fpmgr.isNaN(x)), bvmgr.equal(signBit, bvmgr.makeBitvector(1, 1))));
451+
}
452+
453+
@Test
454+
public void specialValueFunctionsFrom64Bits2() throws SolverException, InterruptedException {
455+
requireBitvectors();
456+
requireFPToBitvector();
457+
458+
final FloatingPointFormula x = fpmgr.makeVariable("x64", doublePrecType);
459+
final BitvectorFormula signBit = bvmgr.extract(fpmgr.toIeeeBitvector(x), 63, 63);
460+
final BitvectorFormula exponent = bvmgr.extract(fpmgr.toIeeeBitvector(x), 62, 52);
461+
final BitvectorFormula mantissa = bvmgr.extract(fpmgr.toIeeeBitvector(x), 51, 0);
462+
463+
assertThatFormula(fpmgr.isInfinity(x))
464+
.isEquivalentTo(
465+
bmgr.or(
466+
bvmgr.equal(
467+
fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(64, 0x7ff0_0000_0000_0000L)),
468+
bvmgr.equal(
469+
fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(64, 0xfff0_0000_0000_0000L))));
470+
471+
assertThatFormula(fpmgr.isZero(x))
472+
.isEquivalentTo(
473+
bmgr.or(
474+
bvmgr.equal(
475+
fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(64, 0x0000_0000_0000_0000L)),
476+
bvmgr.equal(
477+
fpmgr.toIeeeBitvector(x), bvmgr.makeBitvector(64, 0x8000_0000_0000_0000L))));
478+
479+
assertThatFormula(fpmgr.isNormal(x))
480+
.isEquivalentTo(
481+
bmgr.and(
482+
bmgr.not(bvmgr.equal(exponent, bvmgr.makeBitvector(11, 0))),
483+
bmgr.not(bvmgr.equal(exponent, bvmgr.makeBitvector(11, -1)))));
484+
485+
assertThatFormula(fpmgr.isSubnormal(x))
486+
.isEquivalentTo(
487+
bmgr.and(
488+
bvmgr.equal(exponent, bvmgr.makeBitvector(11, 0)),
489+
bmgr.not(bvmgr.equal(mantissa, bvmgr.makeBitvector(52, 0)))));
490+
491+
assertThatFormula(fpmgr.isNaN(x))
492+
.isEquivalentTo(
493+
bmgr.and(
494+
bvmgr.equal(exponent, bvmgr.makeBitvector(11, -1)),
495+
bmgr.not(bvmgr.equal(mantissa, bvmgr.makeBitvector(52, 0)))));
496+
497+
assertThatFormula(fpmgr.isNegative(x))
498+
.isEquivalentTo(
499+
bmgr.and(bmgr.not(fpmgr.isNaN(x)), bvmgr.equal(signBit, bvmgr.makeBitvector(1, 1))));
500+
}
501+
323502
@Test
324503
public void specialDoubles() throws SolverException, InterruptedException {
325504
assertThatFormula(fpmgr.assignment(fpmgr.makeNumber(Double.NaN, singlePrecType), nan))
@@ -777,10 +956,7 @@ public void fpTraversalWithRoundingMode() {
777956

778957
@Test
779958
public void fpIeeeConversionTypes() {
780-
assume()
781-
.withMessage("FP-to-BV conversion not available for CVC4 and CVC5")
782-
.that(solverToUse())
783-
.isNoneOf(Solvers.CVC4, Solvers.CVC5);
959+
requireFPToBitvector();
784960

785961
FloatingPointFormula var = fpmgr.makeVariable("var", singlePrecType);
786962
assertThat(mgr.getFormulaType(fpmgr.toIeeeBitvector(var)))
@@ -789,10 +965,7 @@ public void fpIeeeConversionTypes() {
789965

790966
@Test
791967
public void fpIeeeConversion() throws SolverException, InterruptedException {
792-
assume()
793-
.withMessage("FP-to-BV conversion not available for CVC4 and CVC5")
794-
.that(solverToUse())
795-
.isNoneOf(Solvers.CVC4, Solvers.CVC5);
968+
requireFPToBitvector();
796969

797970
FloatingPointFormula var = fpmgr.makeVariable("var", singlePrecType);
798971
assertThatFormula(
@@ -803,10 +976,7 @@ public void fpIeeeConversion() throws SolverException, InterruptedException {
803976

804977
@Test
805978
public void ieeeFpConversion() throws SolverException, InterruptedException {
806-
assume()
807-
.withMessage("FP-to-BV conversion not available for CVC4 and CVC5")
808-
.that(solverToUse())
809-
.isNoneOf(Solvers.CVC4, Solvers.CVC5);
979+
requireFPToBitvector();
810980

811981
BitvectorFormula var = bvmgr.makeBitvector(32, 123456789);
812982
assertThatFormula(
@@ -876,10 +1046,7 @@ public void checkIeeeBv2FpConversion64() throws SolverException, InterruptedExce
8761046

8771047
@Test
8781048
public void checkIeeeFp2BvConversion32() throws SolverException, InterruptedException {
879-
assume()
880-
.withMessage("FP-to-BV conversion not available for CVC4 and CVC5")
881-
.that(solverToUse())
882-
.isNoneOf(Solvers.CVC4, Solvers.CVC5);
1049+
requireFPToBitvector();
8831050

8841051
proveForAll(
8851052
// makeBV(value.bits) == fromFP(makeFP(value.float))
@@ -892,10 +1059,7 @@ public void checkIeeeFp2BvConversion32() throws SolverException, InterruptedExce
8921059

8931060
@Test
8941061
public void checkIeeeFp2BvConversion64() throws SolverException, InterruptedException {
895-
assume()
896-
.withMessage("FP-to-BV conversion not available for CVC4 and CVC5")
897-
.that(solverToUse())
898-
.isNoneOf(Solvers.CVC4, Solvers.CVC5);
1062+
requireFPToBitvector();
8991063

9001064
proveForAll(
9011065
// makeBV(value.bits) == fromFP(makeFP(value.float))

0 commit comments

Comments
 (0)