Proofs about some float operations

This commit is contained in:
Jay Lorch
2026-03-03 14:43:45 -08:00
parent 4865364b48
commit 6ab452989f
3 changed files with 72 additions and 9 deletions

8
Cargo.lock generated
View File

@@ -1613,11 +1613,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "verus_builtin"
version = "0.0.0-2026-02-24-1505"
version = "0.0.0-2026-03-01-0109"
[[package]]
name = "verus_builtin_macros"
version = "0.0.0-2026-02-24-1505"
version = "0.0.0-2026-03-01-0109"
dependencies = [
"proc-macro2 1.0.106",
"quote 1.0.44",
@@ -1637,7 +1637,7 @@ dependencies = [
[[package]]
name = "verus_state_machines_macros"
version = "0.0.0-2026-02-24-1505"
version = "0.0.0-2026-03-01-0109"
dependencies = [
"indexmap 1.9.3",
"proc-macro2 1.0.106",
@@ -1662,7 +1662,7 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "vstd"
version = "0.0.0-2026-02-24-1505"
version = "0.0.0-2026-03-01-0109"
dependencies = [
"verus_builtin",
"verus_builtin_macros",

View File

@@ -28,7 +28,9 @@ use serde::ser::Serializer;
use serde::Serialize;
use vstd::prelude::*;
use vstd::float::*;
use vstd::std_specs::convert::*;
use vstd::std_specs::cmp::*;
use crate::*;
use crate::verusspec::bigint::*;
@@ -76,7 +78,10 @@ impl View for Number
}
impl Number {
fn from_bigint_owned(value: BigInt) -> Self {
fn from_bigint_owned(value: BigInt) -> (result: Self)
ensures
result@ == NumberView::Integer(value@),
{
if value.is_zero() {
return Number::Int(0);
}
@@ -94,7 +99,10 @@ impl Number {
Number::BigInt(Rc::new(value))
}
fn from_i128(value: i128) -> Self {
fn from_i128(value: i128) -> (result: Self)
ensures
result@ == NumberView::Integer(value as int),
{
if value >= 0 {
if let Ok(u) = u64::try_from(value) {
return Number::UInt(u);
@@ -108,9 +116,43 @@ impl Number {
}
}
spec fn spec_float_to_small_int(value: f64) -> Option<int>
{
if !spec_f64_is_finite(value) ||
!spec_f64_fract(value).eq_spec(&0.0f64) ||
spec_f64_abs(value).partial_cmp_spec(&F64_SAFE_INTEGER) == Some(Ordering::Greater) {
None
}
else {
match value.partial_cmp_spec(&0.0) {
Some(Ordering::Greater) | Some(Ordering::Equal) =>
if spec_u64_as_f64(spec_f64_as_u64(value)).eq_spec(&value) {
Some(spec_f64_as_u64(value) as int)
}
else {
None
},
Some(Ordering::Less) | None =>
if spec_i64_as_f64(spec_f64_as_i64(value)).eq_spec(&value) {
Some(spec_f64_as_i64(value) as int)
}
else {
None
},
}
}
}
fn to_bigint_owned(&self) -> (res: Option<BigInt>)
ensures
self@ matches NumberView::Integer(n) ==> res matches Some(b) && b@ == n,
match self@ {
NumberView::Integer(n) => res matches Some(bi) && bi@ == n,
NumberView::Float(f) =>
match Self::spec_float_to_small_int(f) {
Some(n) => res matches Some(bi) && bi@ == n,
None => res is None,
},
},
{
match self {
Number::UInt(v) => Some(BigInt::from(*v)),
@@ -120,7 +162,18 @@ impl Number {
}
}
fn float_to_small_bigint(value: f64) -> Option<BigInt> {
fn float_to_small_bigint(value: f64) -> (res: Option<BigInt>)
ensures
match Self::spec_float_to_small_int(value) {
Some(i) => res matches Some(bi) && bi@ == i,
None => res is None,
},
{
proof {
axiom_f64_obeys_eq_spec();
axiom_f64_obeys_partial_cmp_spec();
}
if !value.is_finite() || value.fract() != 0.0 {
return None;
}
@@ -144,7 +197,7 @@ impl Number {
None
}
fn to_bigint_rc(&self) -> Option<Rc<BigInt>> {
fn to_bigint_rc(&self) -> (res: Option<Rc<BigInt>>) {
match self {
Number::BigInt(v) => Some(v.clone()),
_ => self.to_bigint_owned().map(Rc::new),

View File

@@ -16,6 +16,16 @@ use vstd::prelude::*;
verus! {
pub axiom fn axiom_f64_obeys_eq_spec()
ensures
<f64 as vstd::std_specs::cmp::PartialEqSpec>::obeys_eq_spec(),
;
pub axiom fn axiom_f64_obeys_partial_cmp_spec()
ensures
<f64 as vstd::std_specs::cmp::PartialOrdSpec>::obeys_partial_cmp_spec(),
;
pub uninterp spec fn spec_f64_as_u64(f: f64) -> u64;
#[inline]