5

I've just started learning Kotlin and I came across a weird sentence in the documentation of basic types:

-0.0 is considered less than 0.0

I understand that their values will not be the same in ones' complement, but I don't know how it might be used in code.

Abdallah Alaraby
  • 2,222
  • 2
  • 18
  • 30
  • 1
    I'm pretty sure that's referring to floating point numbers. 0.0 and -0.0 have different representations (0x00000000 vs 0x80000000). – Michael Feb 04 '18 at 15:38
  • 2
    *Informally, one may use the notation “−0” for a negative value that was rounded to zero.* in https://en.wikipedia.org/wiki/Signed_zero – Naetmul Feb 04 '18 at 16:13
  • Sure, but rounding to +0 is also correct, right? – Abdallah Alaraby Feb 05 '18 at 08:59

3 Answers3

7

The primary purpose of the erased floating point comparison not following the IEEE 754 standard is, when you use floating point numbers in collections and as keys for sorting, you don't want the values that are equal according to the standard to mix with each other. For example, you don't want to mix -0.0 and 0.0 as keys in a map (you might want two different values for these keys).

Likewise, you want a map to match a NaN with itself, despite the standard stating that NaN != NaN.

And, when you sort a set of items, you want a NaN to be properly ordered with other numbers, even though the standard says it's incomparable with other elements (following the standard here might even break the sorting algorithm).

Note that these rules only apply when the objects are not statically known to belong to the floating point types, which, practically, matches the generic use cases and collections. The mathematical use cases, on contrary, usually operate with the numeric types directly (not erasing them to Any or a type parameter) and, accordingly, the IEEE 754 rules are applied.

hotkey
  • 140,743
  • 39
  • 371
  • 326
0

I found that sentence too, and it is puzzling. It really has little to do with the fact zero has two representations, since (one might think) in theory this should be hidden inside the implementation such that -0.0 == +0.0 is true (maybe not ===), and likewise -0.0 < +0.0 and +0.0 < -0.0 are both false).

However, there are some good reasons for declaring -0 < +0, although generally these are pretty obscure. The IEEE 754 standard specifies which should be returned from what, and also has residual (generally settable, but also generally global) control switches for rounding modes etc., and (IIRC) also for controlling whether -0.0 < +0.0 or ==.

I'm sure the Kotlin folks thought this through thoroughly (<- tongue twister), since nobody would design a language feature this way without a lot of hard thought and experience. I can imagine programmers getting quite confused by this in some cases (e.g. finding spurious extra roots in polynomials etc.), but if the programmer is sufficiently IEEE aware, and understands where and when the distinction arises, it seems it shouldn't cause too many problems (except for programmers who skip that part of the documentation!).

And of course programmers must be extra careful using equality on floating point, but until now I always thought things like "if (!!x) return 1.0/x" would be more or less safe somehow (given IEEE's careful attention to rounding, overflow, etc.). Maybe this is discussed elsewhere in the Kotlin docs; I still feel a little queasy about this feature.

d00t
  • 31
  • 4
  • 1
    I just checked IEEE 754, and unless I am missing something, this violates the standard, which requires the two zeros to compare equal. – d00t Jul 17 '21 at 06:33
0

I don't understand the first answer above at all, although I admit I am new to Kotlin.

I have no idea what "the erased floating point comparison" means, but I am assuming it means the decision that -0.0 != +0.0. I see it as an implementation decision, not an erasure.

I also don't understand at all what the last paragraph means. Changing the semantics of equality for a type based on whether it's statically determinable or not is absurd. I can't guess what Kotlin designers had in mind, but it can't possibly be like this; I'm just not understanding what's going on in that paragraph at all.

Also, violating IEEE numerics to allow floating map keys, or provide extra sentinel or flag or key values, is a bizarre priority inversion. That's what NaNs are for. Extraordinary violations of such a widely used standard require extraordinary motivation, and the first answer above seems to miss that boat.

Much better would be to use the underlying 32-, 64-, or 128-bit bitstrings as keys if you need to play such bizarre games. In 45 years of doing real-time numerical processing I have never even wanted to index a map with floats, although I actually can imagine it (but not changing a standard to accommodate such kludges).

Maybe I'm out to lunch, but there seems to be a serious disconnect here somewhere.

d00t
  • 31
  • 4
  • 4
    Couldn't this 'commentary' have been added into your other answer? – Adrian Mole Jul 17 '21 at 19:11
  • Yes, it likely should have been. I'm still a noob at the protocol, and I don't understand the site very well. This was the first time I ever used Stack Overflow for anything nontrivial, and I apologize. – d00t Jul 22 '21 at 16:31
  • Geez, this is the fourth time I've tried to add this comment. Apparently hitting enter ignores most of my typing and submits a small part of it. Also, this last time my entire comment somehow got deleted. Apparently there's some key (near the space bar) I hit by mistake that aborts my current answer and deletes the previously entered partial comment. I found the edit link, which is very cool, except for some reason my entire attempts at earlier versions lost that capability before they were removed entirely. I'm sure I'll figure out what I need to eventually. – d00t Jul 22 '21 at 16:47