3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-02-28 19:01:29 +00:00

Add fpa regression test for fp.to_real denormal encoding fix

Adds src/test/fpa.cpp with test_fp_to_real_denormal() exercising the
bug reported in the issue: denormal floating-point values in
(_ FloatingPoint 2 24) were getting wrong fp.to_real values because
mk_to_real was not subtracting the normalization shift lz from the
exponent.

Tests verify:
- The specific denormal from the bug report is NOT > 1.0
- Two other denormals have correct real values (0.5 and 0.125)
- A normal value is correctly identified as > 1.0

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-02-25 22:04:03 +00:00
parent de3cf18899
commit add65451fd
3 changed files with 75 additions and 0 deletions

View file

@ -58,6 +58,7 @@ add_executable(test-z3
f2n.cpp
finite_set.cpp
finite_set_rewriter.cpp
fpa.cpp
factor_rewriter.cpp
finder.cpp
fixed_bit_vector.cpp

73
src/test/fpa.cpp Normal file
View file

@ -0,0 +1,73 @@
/*++
Copyright (c) 2025 Microsoft Corporation
--*/
// Regression tests for floating-point arithmetic encoding and model generation.
#include "api/z3.h"
#include "util/debug.h"
#include <string.h>
// Test that fp.to_real produces correct values for denormal floating-point numbers.
// Regression test for: incorrect model with (_ FloatingPoint 2 24) and fp.to_real.
// Denormal numbers require subtracting the normalization shift (lz) from the exponent;
// without this fix, denormals in fp.to_real were ~2^lz times too large.
static void test_fp_to_real_denormal() {
Z3_context ctx;
// Test 1: the specific denormal from the bug report (fp #b0 #b00 #b00111011011111001011101)
// has fp.to_real ~= 0.232, which must NOT be > 1.0
ctx = Z3_mk_context(nullptr);
{
const char * spec =
"(assert (> (fp.to_real (fp #b0 #b00 #b00111011011111001011101)) 1.0))\n"
"(check-sat)\n";
const char * result = Z3_eval_smtlib2_string(ctx, spec);
ENSURE(strstr(result, "unsat") != nullptr);
}
Z3_del_context(ctx);
// Test 2: denormal with leading significand bit = 1, fp.to_real should be 0.5
// (fp #b0 #b00 #b10000000000000000000000) in (_ FloatingPoint 2 24)
ctx = Z3_mk_context(nullptr);
{
const char * spec =
"(assert (= (fp.to_real (fp #b0 #b00 #b10000000000000000000000)) (/ 1.0 2.0)))\n"
"(check-sat)\n";
const char * result = Z3_eval_smtlib2_string(ctx, spec);
ENSURE(strstr(result, "sat") != nullptr);
ENSURE(strstr(result, "unsat") == nullptr);
}
Z3_del_context(ctx);
// Test 3: denormal with significand bit pattern giving fp.to_real = 0.125
// (fp #b0 #b00 #b00100000000000000000000) in (_ FloatingPoint 2 24)
ctx = Z3_mk_context(nullptr);
{
const char * spec =
"(assert (= (fp.to_real (fp #b0 #b00 #b00100000000000000000000)) (/ 1.0 8.0)))\n"
"(check-sat)\n";
const char * result = Z3_eval_smtlib2_string(ctx, spec);
ENSURE(strstr(result, "sat") != nullptr);
ENSURE(strstr(result, "unsat") == nullptr);
}
Z3_del_context(ctx);
// Test 4: a normal value (fp #b0 #b01 #b11111111111111111111111) must be > 1.0
// This is the maximum finite normal number in (_ FloatingPoint 2 24)
ctx = Z3_mk_context(nullptr);
{
const char * spec =
"(assert (> (fp.to_real (fp #b0 #b01 #b11111111111111111111111)) 1.0))\n"
"(check-sat)\n";
const char * result = Z3_eval_smtlib2_string(ctx, spec);
ENSURE(strstr(result, "sat") != nullptr);
ENSURE(strstr(result, "unsat") == nullptr);
}
Z3_del_context(ctx);
}
void tst_fpa() {
test_fp_to_real_denormal();
}

View file

@ -287,4 +287,5 @@ int main(int argc, char ** argv) {
TST(ho_matcher);
TST(finite_set);
TST(finite_set_rewriter);
TST(fpa);
}