Zulip Chat Archive

Stream: lean4

Topic: DecidableEq for Float?


Geoffrey Irving (Jan 06 2024 at 20:08):

It looks like Float doesn't have a DecidableEq instance. Is that right, and is it intentional if so? I realize it can't have LawfulBEq due to NaN, but that doesn't seem like it should block DecidableEq.

Geoffrey Irving (Jan 06 2024 at 20:19):

I suppose it would be easy to accidentally use = instead of == if DecidableEq existed, with resulting different behavior.

Ruben Van de Velde (Jan 06 2024 at 20:44):

If it did, it would have to NaN = NaN, right?

Geoffrey Irving (Jan 06 2024 at 20:44):

Yes, by rfl.

Eric Wieser (Jan 06 2024 at 20:53):

DecidableEq is impossible for Float because you can't prove anything at all about float

Geoffrey Irving (Jan 06 2024 at 20:54):

https://leanprover-community.github.io/mathlib4_docs/Init/Data/Float.html#Float.decLt

Geoffrey Irving (Jan 06 2024 at 20:54):

Are you thinking of Lean 3?

Eric Wieser (Jan 06 2024 at 20:54):

Oh, I guess that's not quite true; you can prove everything that's in docs#FloatSpec

Eric Wieser (Jan 06 2024 at 20:56):

So it is indeed weird that DecidableEq is not in that structure, when everything else is

Geoffrey Irving (Jan 06 2024 at 20:57):

I think the foot canon of = vs. == when writing Float code might explain it. I think it may be weird formally but not weird in practice because of that.

Henrik Böving (Jan 06 2024 at 20:58):

Geoffrey Irving said:

I think the foot canon of = vs. == when writing Float code might explain it. I think it may be weird formally but not weird in practice because of that.

But how would you treat the NaN case? NaN is not supposed to be equal to itself.

Eric Wieser (Jan 06 2024 at 20:58):

Sure, but plenty of languages fail at that anyway

Geoffrey Irving (Jan 06 2024 at 20:58):

We're not talking about changing any semantics: it has to be true that NaN = NaN and NaN != NaN.

Geoffrey Irving (Jan 06 2024 at 20:59):

And it is also true as a meta-mathematical fact that = is decidable over Float.

Geoffrey Irving (Jan 06 2024 at 20:59):

The only question is whether to make that fact available to programmers.

Eric Wieser (Jan 06 2024 at 21:00):

Eric Wieser said:

Sure, but plenty of languages fail at that anyway

To elaborate, python has a stronger version of == that sometimes surfaces from behind the scenes:

>>> nan = float('nan')
>>> d = {nan: 'oh no'}
>>> d[nan]
'oh no'
>>> d[float('nan')]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: nan
>>>

Eric Wieser (Jan 06 2024 at 21:01):

If we want to guard against the = footgun on floats, that presumably can be handled by a linter; I don't think it is useful to cripple the = notation (by making it need open Classical, as it does today)

Eric Wieser (Jan 06 2024 at 21:05):

Here's a hack in the meantime that avoids needing to change core:

import Mathlib.Logic.Basic

unsafe def instDecidableEqFloatStupid : DecidableEq Float := fun f g => if toString f == toString g then .isTrue lcProof else .isFalse lcProof

@[instance, implemented_by instDecidableEqFloatStupid]
opaque instDecidableEqFloat : DecidableEq Float := Classical.decEq _

Eric Wieser (Jan 06 2024 at 21:06):

(it would be great if https://github.com/pygments/pygments/pull/2618 could land so that unsafe and opaque got highlighted there...)

Geoffrey Irving (Jan 06 2024 at 21:07):

How does that work? I don't think Float.toString is injective.

Geoffrey Irving (Jan 06 2024 at 21:09):

And of course, if we implement this, we do want the fast native version.

Eric Wieser (Jan 06 2024 at 21:13):

Geoffrey Irving said:

How does that work? I don't think Float.toString is injective.

Ah, I guess we're in trouble because of how many different NaNs there are, which indeed share a toString.

Geoffrey Irving (Jan 06 2024 at 21:14):

Well, that's one problem...

#eval (1e-10 : Float).toString
-- "0.000000"

Eric Wieser (Jan 06 2024 at 21:17):

Oh, I naively assumed that Lean would be using something like dragon4 for unambiguous (finite) float printing like NumPy does

Geoffrey Irving (Jan 06 2024 at 21:18):

That would be better, I agree. Though unrelated to DecidableEq which maps to a single assembly instruction.

Geoffrey Irving (Jan 06 2024 at 21:18):

I would certainly prefer it if 1e-10 didn't show up as 0, as it makes unit tests slightly harder to debug.

Eric Wieser (Jan 06 2024 at 21:19):

At risk of further derailment; is there any builtin way to print 1e-10 at higher precision?

Geoffrey Irving (Jan 06 2024 at 21:21):

printf("%.17g", x) is (mostly) injective in C, so if we have access to format strings then yes.

Mario Carneiro (Jan 09 2024 at 02:30):

@Eric Wieser There is an implementation of toStringFull either in zulip or in std (std4#282)

Mario Carneiro (Jan 09 2024 at 02:34):

My understanding of the lean model of Float is that it identifies all NaNs (sign included) and otherwise distinguishes all bit patterns (including 0 vs -0)


Last updated: May 02 2025 at 03:31 UTC