Zulip Chat Archive

Stream: Is there code for X?

Topic: Proving nan != nan


Leni Aniva (Feb 11 2025 at 23:59):

Is there a way to prove this?

def nan := 0.0 / 0.0
#eval nan != nan
example : nan != nan := by
  decide

Evaluating nan != nan directly gives a boolean answer

Mario Carneiro (Feb 12 2025 at 00:06):

no

Mario Carneiro (Feb 12 2025 at 00:06):

well, you can use native_decide

Mario Carneiro (Feb 12 2025 at 00:06):

but the float functions are otherwise opaque

Leni Aniva (Feb 12 2025 at 00:08):

Mario Carneiro said:

well, you can use native_decide

How does this thing work? When I use #print I only see an _auxLemma

Mario Carneiro (Feb 12 2025 at 00:11):

it runs eval and trusts the result

Notification Bot (Feb 12 2025 at 00:11):

Leni Aniva has marked this topic as resolved.

Mario Carneiro (Feb 12 2025 at 00:12):

the underlying mechanism is docs#Lean.reduceBool, which is an opaque function which, when reduced in the kernel, will run the interpreter on the argument, plus an axiom saying that it is the identity function (and hence the eval result is consistent with the actual definition of the function)

Brando Miranda (Feb 12 2025 at 19:37):

Apologies for unresolving the topic.

I'm trying to understand if it is ok to use == in my theorems. An example theorem:

example:2.0==2.0:=bysafe_tactic_please

But the description of native_decide sounds scary:

native_decide is a synonym for decide +native. It will attempt to prove a goal of type p by synthesizing an instance of Decidable p and then evaluating it to isTrue ... Unlike decide, this uses #eval to evaluate the decidability instance.

This should be used with care because it adds the entire lean compiler to the trusted part, and the axiom Lean.ofReduceBool will show up in #print axioms for theorems using this method or anything that transitively depends on them. Nevertheless, because it is compiled, this can be significantly more efficient than using decide, and for very large computations this is one way to run external programs and trust the result.

example : (List.range 1000).length = 1000 := by native_decide

is it really safe to use native_decide?

Brando Miranda (Feb 12 2025 at 19:41):

if I want to make a universal theorem about floats, is the approach to do:

For all floats except nan, some property f holds?

eg pseudo Lean4

theorem uni : \forall x : Float, f x == 0.0, f x <= 1.0, 0.0 <= f x := by
-- 1 native_decide
-- 2, 3 more tactics that work

PS: How do I say, except Nan? Or make theorems about floats?

Kyle Miller (Feb 12 2025 at 19:52):

Floats are tricky to prove anything about, since they're opaque and have no axioms. Using native_decide pulls in the runtime characteristics of Float to prove things.

Something you might consider instead is to work with some abstract type that has the axioms you need (maybe a docs#LinearOrderedRing ?), then you can pretend that Float satisfies these axioms at evaluation time. It's not 100% correct to do this though, since it's an approximation (for example, with floats a + b = a does not imply b = 0). I think @Tomas Skrivan has experience with this in SciLean.

Brando Miranda (Feb 12 2025 at 20:07):

Not ideal, but I think I'm ok for now to use runtime characteristics to do proofs (or at least theorems). What I'm confused is that I can't seem to identify the pattern of when to use = vs ==. Eg

#eval 2.0
example : 2.0 = 2.0 := by rfl
-- example : 2.0 = 2.0 := by native_decide -- FAILS

#eval 2.0
-- example : 2.0 == 2.0 := by rfl -- FAILS
example : 2.0 == 2.0 := by native_decide

Brando Miranda (Feb 12 2025 at 20:09):

Ok so from my above "experiment" I think == and by native_decide seems to always work while rfl does not always work.

Brando Miranda (Feb 12 2025 at 20:10):

Ok I think == with by naitve_decide seems to satisfy the English specs I said ("I'm ok with run time evals")

Brando Miranda (Feb 12 2025 at 20:11):

Since decidable equality with floats doesn't exist so I think = and rfl doesn't always work? (though idk of an example).

Kyle Miller (Feb 12 2025 at 20:15):

rfl can prove 2.0 = 2.0 since the LHS and RHS are identical, so Eq.refl can apply.

You're correct that there's no DecidableEq instance for Float, so it's not possible to synthesize an algorithm to calculate whether or not 2.0 = 2.0.

The reason 2.0 == 2.0 can be evaluated is that == is Bool-valued, so there's no need to synthesize an algorithm (== is already an algorithm, and it uses the runtime == for Float)

Brando Miranda (Feb 12 2025 at 20:25):

Ignoring the nan issue (ie nan != nan), which is a big issue I think for my goals (doing universal theorems about floats). Can universal theorems about floats be made with ==?

E.g.

def truncateNumber (x : Float) : Float :=
  x - Float.floor x

theorem truncateNumber_correct (x : Float) :
  truncateNumber x = x - Float.floor x  0  truncateNumber x  truncateNumber x < 1 := sorry

Is = or == the right thing to do?

PS: I know it's a little silly, but I'm just trying to figure out how to make universal theorems about floats that are sensible (even if not perfect). If possible.

Kyle Miller (Feb 12 2025 at 20:28):

I'd think this isn't possible

Brando Miranda (Feb 12 2025 at 20:34):

Kyle Miller said:

I'd think this isn't possible

what about this hack?

def nan := 0.0 / 0.0

theorem truncateNumber_correct (x : Float) :
  x  nan  truncateNumber x = x - Float.floor x  0  truncateNumber x  truncateNumber x < 1 := sorry

since rfl works on floats per your comment, it seems the above is an ok candidate?

Kyle Miller (Feb 12 2025 at 20:35):

(Potentially one could admit a decision procedure as an extra axiom that checks your theorem for all possible x; there are at most 2^64 values, which is theoretically possible to exhaust, but practically you might not want to wait that long.)

Brando Miranda (Feb 12 2025 at 20:35):

ah I see, so the issue is that evaluate needs an actual float to work.

Kyle Miller (Feb 12 2025 at 20:36):

In truncateNumber_correct, you can prove truncateNumber x = x - Float.floor x using rfl (that's true by definition of truncateNumber), but you can't get the inequalities without additional Float axioms.

Kyle Miller (Feb 12 2025 at 20:38):

You could add Float axioms yourself though. Maybe it's worth doing? You'll have to be very careful since these won't be machine-verified; though with some effort you could do some testing to see they aren't immediately refuted.

Brando Miranda (Feb 12 2025 at 20:42):

I don't know if I have time to do that (or if I'm the right person). Let me think about it. I think I'm ok at least with a sorry proof that uses the correct version of = or == for now.

Brando Miranda (Feb 12 2025 at 20:49):

as a ugly hack I could do an existential quantifier and use == for the first one, but it wouldn't fix the inequalities issues (since the rest of the infrastructure for floats doesn't exist)

Brando Miranda (Feb 12 2025 at 20:50):

actually even an existential might work for inequalities:

-- 1) Basic test case: 3.5 → 0.5
#eval truncateNumber 3.5
example : truncateNumber 3.5 == 0.5 := by native_decide

-- 2) Small decimal part: 1.33 → 0.33
#eval truncateNumber 1.33
example : Float.abs (truncateNumber 1.33 - 0.33) < 1e-6 := by native_decide

but it's very hacky

Brando Miranda (Feb 12 2025 at 20:57):

I think

theorem truncateNumber_correct (x : Float) :
  ¬ Float.isNaN x  ¬ Float.isInf x 
  truncateNumber x = x - Float.floor x  0  truncateNumber x  truncateNumber x < 1 := sorry

is ok? (after I hypothetically make the Floats library properly some day)

Brando Miranda (Feb 12 2025 at 20:58):

does floats exist in Isabelle or Coq perhaps to help me?

Brando Miranda (Feb 12 2025 at 20:58):

or a close language to Lean 4

Mario Carneiro (Feb 12 2025 at 22:23):

coq has a nice float library called flocq

Aaron Liu (Feb 12 2025 at 23:41):

Just curious, why do you need theorems about Floats?

Brando Miranda (Feb 13 2025 at 00:32):

as far as I know, Real numbers don't exist in real programs used in practice and I wanted to formalize programs more similar to real programs used in practice in Lean 4 (I admit I'd likely start with very toy examples)

Brando Miranda (Feb 13 2025 at 00:32):

Mario Carneiro said:

coq has a nice float library called flocq

amazing! thank you Mario!

Chris Wong (Feb 13 2025 at 01:44):

Skimming the flocq docs, it looks like it builds a model of floating point from scratch, without referencing the native type.

Yury G. Kudryashov (Feb 13 2025 at 01:55):

You can try to write axioms about Float that implement some official specifications.

Yury G. Kudryashov (Feb 13 2025 at 01:57):

Or you can write a type class LawfulFloat and assume it whenever you prove something about floats.

Yury G. Kudryashov (Feb 13 2025 at 01:58):

Then one can verify your axioms for a specific system (hardware, OS, compiler) by checking all possible values (probably, takes a lot of time).

Jz Pan (Feb 13 2025 at 05:26):

What about writing an IEEE754 float implementation in Lean? Just like software float library in C/C++ which uses int64 to simulate double.

Yury G. Kudryashov (Feb 13 2025 at 05:51):

AFAIU, you can do it but it won't use the hardware/OS/compiler-provided float, so it will be slower.

Yury G. Kudryashov (Feb 13 2025 at 05:51):

Possibly, you can have both, but I don't know how to do it.

Edward van de Meent (Feb 13 2025 at 06:52):

I seem to recall someone actually working on a verified implementation of float?

Edward van de Meent (Feb 13 2025 at 07:00):

Right, discord user rob_23oba mentioned working on IEEE-754 compliant floats

Markus Himmel (Feb 13 2025 at 07:22):

If at some point there is a successful community library that exposes a typeclass like IsIEEECompliantFloat, provides an instance of this class using a pure Lean implementation of IEEE-754 floats (docs#FP.Float ?) and proves useful theorems about floats, then the FRO will be very interested in exploring ways of modifying Lean's native Float type so that it is possible to instantiate IsIEEECompliantFloat Float.

Jz Pan (Feb 13 2025 at 14:56):

Markus Himmel said:

IsIEEECompliantFloat Float

I don't think this makes sense, as it's not the type Float being IEEE-754 compliant, but the functions on Float are IEEE-754 compliant: add, subtract, etc., and sin, cos, ...

Jz Pan (Feb 13 2025 at 15:09):

Is this declaration ABI correct?

@[unbox]
structure MyFloat : Type where
  ofRaw :: toRaw : UInt64

Mario Carneiro (Feb 13 2025 at 15:51):

no, it would need to be wrapping a Float

Mario Carneiro (Feb 13 2025 at 15:51):

float and double are different from uintN_t in the C ABI

Mario Carneiro (Feb 13 2025 at 15:52):

e.g on intel floats are passed in the float registers in the calling convention

Joseph Myers (Feb 14 2025 at 00:35):

If your structure wraps an integer, that would also imply that you're formalizing at IEEE specification level 4, where the choices of NaN payloads are significant (and those often differ between implementations - and even between different instructions on the same architecture, e.g. x87 and SSE floating point have different rules for choosing the payload resulting from an operation between two NaNs). IEEE specification level 3, where most (not all) things are fully specified in IEEE 754, is probably a better option for formalization for most purposes.

Robin Arnez (Feb 16 2025 at 19:47):

Jz Pan schrieb:

Markus Himmel said:

IsIEEECompliantFloat Float

I don't think this makes sense, as it's not the type Float being IEEE-754 compliant, but the functions on Float are IEEE-754 compliant: add, subtract, etc., and sin, cos, ...

I'd think that IsIEEECompliantFloat α would be a type class like this:

class IsIEEECompliantFloat (α : Type u) (fmt : BinaryFloatFormat) extends Add α, Sub α, Neg α, Mul α, Div α, LE α, LT α, BEq α, α  BinaryFloat fmt where
  sqrt (x : α) : α
  apply_add (x y : α) : toFun (x + y) = toFun x + toFun y
  apply_sub (x y : α) : toFun (x - y) = toFun x - toFun y
  -- ...
  apply_sqrt (x : α) : toFun (sqrt x) = (toFun x).sqrt

And then you could make an instance of it with all of Floats normal functions/instances and there you have your assertion that the Float functions work.
(note that sin, cos, pow, etc. are weird cases because the standard doesn't define them and they are usually defined from the other existing functions, so we don't put those here)

Robin Arnez (Feb 16 2025 at 20:01):

Jz Pan schrieb:

Is this declaration ABI correct?

@[unbox]
structure MyFloat : Type where
  ofRaw :: toRaw : UInt64

Lean already makes a special case for Floats, UInts, etc. so while this is not correct as an ABI, the compiler will take care of that (if your type would be called Float, that is). Like Joseph said, this would imply multiple NaNs which can get in the way of proofs (e.g. proving x + nan = nan would be impossible because it actually isn't true on some platforms where the left hand side would turn out to be a different NaN than the right hand side). I thought of an approach that might solve this though: separate Float into a NativeFloat with multiple NaNs which has a similar definition as yours and then the normal Float which is merely a quotient on NativeFloat that makes all NaNs equal. The advantage of this approach is that we can treat Float and NativeFloat as normal types and use match and everything like usual, similar to how Array works. A definition as an inductive would make this more difficult because then you'd need to emit code that checks isNaN, isInf, etc. and decode everything.

Mario Carneiro (Feb 16 2025 at 20:25):

Lean's functions on Float already check for nans and ensure that you can't get at payload bits using e.g. toBits, which would otherwise result in nondeterminism in opaque functions and hence UB (because lean logical functions must be deterministic)

Mario Carneiro (Feb 16 2025 at 20:26):

The inductive type which is isomorphic to Float is the one where there is exactly one nan value

Mario Carneiro (Feb 16 2025 at 20:28):

I would encourage anyone wanting to improve the state of lean floats to PR something to Batteries

Joseph Myers (Feb 17 2025 at 01:01):

Robin Arnez said:

(note that sin, cos, pow, etc. are weird cases because the standard doesn't define them and they are usually defined from the other existing functions, so we don't put those here)

The standard does define them, as recommended operations (see subclause 9.2). I don't think the difference between recommended and required operations is very important for formalization, except that IEEE 754 is a complicated enough standard that there are many more important operations that would be higher priority. (And if you wanted computable definitions rather than just relying on tactics for computation, computable versions of those functions would be a lot more complicated to write than either noncomputable versions or computable versions of the required operations.)

Jz Pan (Feb 17 2025 at 07:32):

I think the IEEE754 standard permits different NaNs, at least there are sNaN and qNaN, both of them can have payloads, see https://en.wikipedia.org/wiki/Double-precision_floating-point_format and https://en.wikipedia.org/wiki/NaN

Robin Arnez (Feb 17 2025 at 07:42):

Joseph Myers schrieb:

The standard does define them, as recommended operations

Yes but pretty much no implementation of floating point will actually give you the exact result, they will all be incorrect by 1 ulp in a few places. So writing a definition of sin that e.g. works as rounding Real.sin would be probably correct per specification but will still differ from the implementations that are provided by math libraries. This makes it pretty difficult to formalize, since you'd somehow have to write a definition that allows for rounding errors in the actual implementation. Besides, Real.sin is not in core lean, so you'd have to still write some of it yourself.

Robin Arnez (Feb 17 2025 at 07:48):

Jz Pan schrieb:

I think the IEEE754 standard permits different NaNs, at least there are sNaN and qNaN, both of them can have payloads, see https://en.wikipedia.org/wiki/Double-precision_floating-point_format and https://en.wikipedia.org/wiki/NaN

Yes, but multiple NaNs get in the way of proofs because different implementations handle them differently, so nan:0x403124 + nan:0x341003 might be nan:0x403124 or nan:0x741003 or -nan:0x400000 or whatever. You can't really prove useful things about NaN with such a definition.

Jz Pan (Feb 17 2025 at 15:27):

Robin Arnez said:

Jz Pan schrieb:

I think the IEEE754 standard permits different NaNs, at least there are sNaN and qNaN, both of them can have payloads, see https://en.wikipedia.org/wiki/Double-precision_floating-point_format and https://en.wikipedia.org/wiki/NaN

Yes, but multiple NaNs get in the way of proofs because different implementations handle them differently, so nan:0x403124 + nan:0x341003 might be nan:0x403124 or nan:0x741003 or -nan:0x400000 or whatever. You can't really prove useful things about NaN with such a definition.

You can only prove things for a specific software IEEE-754 float implementation. But not the hardware Float.

Jz Pan (Feb 17 2025 at 15:28):

Optionally, native_decide can add axiom that software float is coincide with hardware float when there are no NaNs.

Jz Pan (Feb 17 2025 at 15:29):

... and that could probably prove False easily.

Notification Bot (Feb 17 2025 at 15:48):

Yury G. Kudryashov has marked this topic as unresolved.

Yury G. Kudryashov (Feb 17 2025 at 15:49):

Instead of adding an axiom, you can add a typeclass [LawfulFloat Float] as outlined above. Then your theorems will be conditional to Float being lawful.

Mario Carneiro (Feb 17 2025 at 22:12):

Robin Arnez said:

Yes but pretty much no implementation of floating point will actually give you the exact result, they will all be incorrect by 1 ulp in a few places.

IIRC Float.sin is off by much more than 1 ulp on intel because they got pi wrong in the argument reduction step. I think this is in the spec now(!)

Ruben Van de Velde (Feb 17 2025 at 22:19):

Is that like the Indiana pi bill?

Robin Arnez (Feb 18 2025 at 16:41):

Mario Carneiro schrieb:

I would encourage anyone wanting to improve the state of lean floats to PR something to Batteries

I'd be happy to PR something to Batteries! However, I tried defining a type similar to flocq and ran into issues writing the proof required for the finite constructor (I ended up using Mathlib.Algebra.Order.Ring.Rat and such). The scope for something in batteries would probably rather be an axiomatic definition of the common operations with some lemmas about 0, inf and nan and perhaps some simprocs for reducing the float operations. This would probably mean that there'd be some code duplication for Float vs Float32 though...
For the implementation I'm thinking:

/-- Returns whether `x` is a NaN bit pattern, that is `Float.ofBits x = Float.nan` -/
def Float.isNaNBits (x : UInt64) : Bool := (x >>> 52) &&& 0x7FF = 0x7FF  x &&& 0x000F_FFFF_FFFF_FFFF  0

axiom Float.ofBits_toBits (x : Float) : ofBits x.toBits = x
axiom Float.toBits_ofBits_of_not_isNaNBits (x : UInt64) (h : isNaNBits x = false) : (ofBits x).toBits = x
axiom Float.toBits_ofBits_of_isNaNBits (x : UInt64) (h : isNaNBits x) : (ofBits x).toBits = 0x7FF8_0000_0000_0000

This would still leave it open how we actually define Float (as an inductive, a wrapper around Quot (fun x y => Float.isNaNBits x ∧ Float.isNaNBits y) or as quotient around a float with multiple NaNs or whatever).
Then everything else can be defined using toBits and ofBits.

Robin Arnez (Feb 19 2025 at 17:50):

I've now put my first wip draft with the simplest definitions (ofBits, toBits, isNaN, isFinite, isInf and neg) as batteries#1141 and will probably continue adding definitions and theorems. @Mario Carneiro, if you're interested, I encourage you to take a look.

Robin Arnez (Feb 19 2025 at 18:07):

I've now proven nan != nan within my PR lol

Mario Carneiro (Feb 19 2025 at 18:08):

You should probably merge what you have with FP.Basic in mathlib first

Mario Carneiro (Feb 19 2025 at 18:09):

because that's basically a port of the first few definitions in flocq

Robin Arnez (Feb 19 2025 at 18:10):

I rewrote FP.Basic but I needed Mathlib.Algebra.Order.Ring.Rat. You can probably do it without but it felt too complicated otherwise.

Robin Arnez (Feb 19 2025 at 18:11):

You know, the current definition is full of unsafes

Mario Carneiro (Feb 19 2025 at 18:11):

the unsafes are just because someone was lazy and didn't want to prove some proof obligations

Mario Carneiro (Feb 19 2025 at 18:12):

it's a sorry that gets past the mathlib linter

Robin Arnez (Feb 19 2025 at 18:12):

Basic.lean
if you're interested, the proofs aren't particularly nice though

Robin Arnez (Feb 19 2025 at 18:15):

Maybe you have some ideas how this could be ported to batteries / have a nicer proof.

Mario Carneiro (Feb 19 2025 at 18:16):

hm, it might be better to PR to mathlib first then, if the maintainers are alright with it. I would like to separate out the part about upstreaming bits of rat ring theorems

Mario Carneiro (Feb 19 2025 at 18:17):

it might also be that this is just too annoying without an algebraic hierarchy, in which case it will have to stay in mathlib for the time being

Robin Arnez (Feb 19 2025 at 18:18):

I feel like the most important things for Float are its relationships with Rat or Real and Rat doesn't have a proof framework on batteries, only on mathlib.

Mario Carneiro (Feb 19 2025 at 18:18):

The basic theorems about rat should be upstreamed

Mario Carneiro (Feb 19 2025 at 18:19):

that won't be enough if you need to use ring and linarith, but you can at least do manual proofs using the ring axioms that way

Robin Arnez (Feb 19 2025 at 18:20):

Yeah that would probably be enough for most of what I was doing here (I mean it was mostly a manual proof)

Robin Arnez (Feb 19 2025 at 18:26):

What do you think should happen with the batteries draft PR then? Should I rather put this into a PR to mathlib?

Mario Carneiro (Feb 19 2025 at 18:29):

I think so

Robin Arnez (Feb 19 2025 at 18:33):

Oh, there is also one more thing: I used Nat.divFloat in my definition (for convenience) but not your definition (which was wrong for subnormals and used e.g. scaleB and div through inf and nan which are things we wanted to define). Should I then just make a duplicate Nat.divFloat' in the mathlib pr?

Mario Carneiro (Feb 19 2025 at 18:35):

I'm not sure I follow

Robin Arnez (Feb 19 2025 at 18:37):

Well, you know Nat.divFloat from Batteries.Lean.Float (this one). I defined my own version of it that is (hopefully) correct and works with toBits and ofBits only. However, these two definitions would conflict.

Mario Carneiro (Feb 19 2025 at 18:38):

I don't think the definition should use Float at all

Mario Carneiro (Feb 19 2025 at 18:38):

everything should be about the ordinary non-opaque type FP.Float

Robin Arnez (Feb 19 2025 at 18:38):

I mean the axiomatic redefinition.

Robin Arnez (Feb 19 2025 at 18:39):

Or should I just not care about that right now?

Mario Carneiro (Feb 19 2025 at 18:39):

the first task is to have a good theory of floats completely independent of lean's Float type

Mario Carneiro (Feb 19 2025 at 18:40):

once we have that we can look into what we would need to do to retrofit the theory onto Float via some refactoring and shuffling of theorems between repos

Robin Arnez (Feb 19 2025 at 18:40):

oh, okay, yeah we can do that

Mario Carneiro (Feb 19 2025 at 18:41):

so basically we just want a definition with the right mathematical properties to be a putative alternative definition of Float

Mario Carneiro (Feb 19 2025 at 18:41):

ignoring issues about data representation of inductive types in the compiler

Robin Arnez (Feb 19 2025 at 18:49):

Also, there was an idea I had, which is, instead of defining float (FP.Float) operations through natural number division, define them through an auxiliary PositiveBinaryRealApprox (I have not come up with a better name lol) which is

structure PositiveBinaryRealApprox where
  log2 : Int
  approx (x : Int) : Nat × Bool
  approx_correct (x : Int) : (approx (x - 1)).1 = (approx x).1 / 2
  approx_exact_iff (x : Int) :
    (approx x).2   y > x, (approx y).1 = (approx x).1 <<< (y - x).toNat
  log2_correct (x : Int) : (approx x).1 = 0  x + log2 < 0

The reason for it being computability and having a simple connection to Real and making it easier to have one rounding function def FP.Float.round (x : PositiveBinaryRealApprox) (rm : RoundingMode) : FP.Float. Do you think this is a good idea?

Mario Carneiro (Feb 20 2025 at 21:56):

couldn't you use Rat in place of PositiveBinaryRealApprox for a much simpler spec?

Mario Carneiro (Feb 20 2025 at 21:58):

it also sounds kind of like you are reinventing computable reals

Robin Arnez (Feb 21 2025 at 06:45):

There's sqrt though

Robin Arnez (Feb 21 2025 at 06:55):

And yes, the intention is basically computable reals but this particular version makes rounding simplest I think:

def FP.Float.round (sign : Bool) (x : PositiveBinaryRealApprox) (rm : RoundingMode) : FP.Float fmt :=
  if hinf : fmt.maxExp  x.log2 then .inf sign else

  let exp := max (x.log2 + 1 - fmt.precision) fmt.minExp
  let rounded :=
    match rm with
    | .zero => (x.approx exp).1
    | .ceil => (x.approx exp).1 + (!sign && (x.approx exp).2).toNat
    | .floor => (x.approx exp).1 + (sign && (x.approx exp).2).toNat
    | .nearest_inf => ((x.approx (exp + 1)).1 + 1) / 2
    | .nearest_even =>
      let (val, exact) := x.approx (exp + 1)
      if val % 4 = 1  exact then val / 2 else (val + 1) / 2
  if h2 : rounded  2 ^ fmt.precision then -- overflow
    sorry
  else
    .finite sign exp rounded sorry

Robin Arnez (Feb 21 2025 at 06:56):

The intention is obviously that we treat PositiveBinaryRealApprox like { x : Real // 0 < x } in proofs while having a computable definition.

Robin Arnez (Feb 21 2025 at 07:00):

Robin Arnez schrieb:

There's sqrt though

While sin, cos probably won't be supported through a computable definition, sqrt is still one of the operations that is supported exactly on pretty much all platforms (iirc) and we should have a definition for it.

Robin Arnez (Feb 21 2025 at 07:00):

Also rounding reals in general would also be nice

Mario Carneiro (Feb 21 2025 at 08:21):

so round reals then

Mario Carneiro (Feb 21 2025 at 08:21):

no need to use computable reals here

Robin Arnez (Feb 21 2025 at 20:06):

I guess you're right, we don't have any obligations to make this computable, this just needs to exist for specification and proof purposes. I just feel like it is weird to have everything marked noncomputable when you can compute it in theory. But you're probably right that the spec should probably be just a noncomputable definition and then you can do @[csimp] and whatever for computable definitions later.


Last updated: May 02 2025 at 03:31 UTC