1

Just noticed that Python and JavaScript have exact comparison. Example in Python:

>>> 2**1023+1 > 8.98846567431158E307
True

>>> 2**1023-1 < 8.98846567431158E307
True

And JavaScript:

> 2n**1023n+1n > 8.98846567431158E307
true

> 2n**1023n-1n < 8.98846567431158E307
true

Anything similar available for Java, except converting both arguments to BigDecimal?

Rúben Dias
  • 336
  • 2
  • 9
  • Related: https://stackoverflow.com/q/17960186 – Dawood ibn Kareem Jan 23 '23 at 21:40
  • Now that I've had a couple of hours to think about this, I'm fairly sure that the only ways to compare a `double` to `BigInteger` are to convert them both to `BigDecimal`, or to mess around fishing out sequences of bits. The former is not as bad as it sounds, because there's very little overhead in converting `BigInteger` to `BigDecimal` - all the `BigDecimal` will be is a wrapper around the `BigInteger` that includes things like the scale and the precision. – Dawood ibn Kareem Jan 24 '23 at 00:17
  • Well, you can't really get around that overhead. Even if you could convert your `double` directly to `BigInteger` for the comparison. – Dawood ibn Kareem Jan 24 '23 at 02:34
  • An alternative solution would be to convert the Java double floating point value into a rational number. But I don't know about some rational number integration into the JDK, would possibly need a 3rd party library. – Rúben Dias Jan 26 '23 at 11:20
  • You really wouldn't want to do that. If you're going to compare the number to a BigInteger, you don't care about the fractional part of it. But converting to a rational number and comparing forces you to multiply everything through by a potentially large power of two. – Dawood ibn Kareem Jan 26 '23 at 18:41
  • Power of two is only shifting. Thats still better than what BigDecimal would do. You can just record the power of two shifting, and defer it a little bit, so that you don't create unnecessarily some trivial bigint numbers like 2^n for some n. – Rúben Dias Jan 27 '23 at 19:49
  • 1
    I would be very surprised if you could find (or write) a library for rational numbers, that outperforms `BigDecimal` on comparing a `double` to a `BigInteger`. The algorithm you've outlined in your answer would probably work well, but I don't see that it's in any way related to dealing with rational numbers. – Dawood ibn Kareem Jan 27 '23 at 22:53
  • You can convert a floating point value m*2^-n into the rational number m/2^n. You don't need to make it canonical for comparison. All you need some routines to unpack the the Java double floating point value. And there exists the routine Double.doubleToLongBits() for that purpose. The rest is bit masking and bit shifting to unpack the floating point value. So there is no need to wait for a library, the runtime of Java already supports unpacking. – Rúben Dias Jan 28 '23 at 01:41

1 Answers1

0

Preliminary answer, i.e. verbal solution sketch:

I am skeptical about a solution that would convert to BigDecimal, since this conversion results in a shift of the base from base=2 to base=10. As soon as the exponent of the Java double floating point value is different from the binary precision, this leads to additional digits and lengthy pow() operations, which one can verify by inspecting some open source BigDecimal(double) constructor implementation.

One can get the mantissa via Double.doubleToRawLongBits(d). If the Java double floating point value is not a sub-normal all that needs to be done is (raw & DOUBLE_SNIF_MASK) + (DOUBLE_SNIF_MASK+1) where 0x000fffffffffffffL This means the integer Java primitive type long should be enough to carry the mantissa. The challenge is now to perform a comparison taking the exponent of the float also into account.

But I must admit I didn't have time yet to work out some Java code. I have also in mind some optimizations using bigLength() of the other argument, which is in this setting a BigInteger. The use of bitLength() would speed up the comparison. A simple heuristic can implement a fast path, so that the mantissa can be ignored. Already the exponent of the double and the bitLength() of the BigInteger give enough information for a comparison result.

As soon as I have time and a prototype running, I might publish some Java code fragment here. But maybe somebody faced the problem already. My general hypothesis is that a fast or even ultra fast routine is possible. But I didn't have much time to search the internet and to find an implementation, thats why I defered the problem to stack overflow, maybe somebody else had the same problem as well and/or might point to a complete solution?

Rúben Dias
  • 336
  • 2
  • 9