Skip to content

Java: in correct rounding to two digits #83

Description

@ulfjack

The Java implementation sometimes doesn't output the closest two-digit representation of a number, but instead rounds to one digit (correctly), and then appends a '0'.

Known examples:
1.0E-323 should be 9.9E-324
1.0E-322 should be 9.9E-323

Activity

  1. added
    bugSomething isn't working
    JavaAffects the Java implementation in src/main/java.
    on Dec 27, 2018
  2. LeeTibbert commented on Feb 25, 2019

    @LeeTibbert

    @ulfjack

    I just tried to replicate this issue as part of my efforts to port RYU to Scala Native. Please pardon
    any lack of understanding or stupidity on my part, When I use https://repl.it/languages/java to run the java code

    class Main {
      public static void main(String[] args) {
        System.out.println(Double.toString(1.0E-323));
        System.out.println(Double.toString(9.9E-324));
      }
    }
    

    I get the same output 1.0E-323 for both 1.0E-323 and 9.9E-324.
    That is the output reported as incorrect/buggy in this issue. Seems
    like Java did not get the memo.

    Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)
    1.0E-323
    1.0E-323

    Using the web site avoids an peculiarities on my local machine but uses
    an old version of Java 8.

    I get the same results when I run the same code on my local 64 bit machine running
    'openjdk 11.0.1 2018-10-16'

    Does RYU truely have a bug here or am I not seeing something, either subtle or obvious?

    Thank you,

    Lee

    PS: The rest of the RYU port to Scala Native seems to be running just fine. Thank you for RYU.

  3. ulfjack commented on Feb 27, 2019

    @ulfjack
    OwnerAuthor

    Well, whether the current output is correct or not depends on what exactly the specification for Double.toString says. Unfortunately, it's not very precise in a mathematical sense, and this is a rather esoteric corner case. The spec basically says that we should print (a) the shortest number, with (b) at least two digits, that (c) parses back to the original number and (d) is closest to the exact value.

    Ignoring (b), the shortest number would clearly be 1e-323. The crux of the matter is the requirement (b).

    Should we compute the shortest number, and then print two digits? Then it should be 1.0e-323.

    Should we compute the two-digit number that is closest to the original input? Then it should be 9.9e-324.

    Clearly, we have to pick one of these interpretations. In my opinion, the second interpretation is slightly preferable, that is, I prefer the printed number to be slightly closer to the exact value. We have two digits, so why not use them? There was a brief discussion of this on one of the OpenJDK mailing lists, and there was no strong argument against this interpretation.

    And yes, that implies that the current OpenJDK implementation of Double.toString is incorrect. There are actually more cases where it is incorrect, and more clearly so - there are some cases where it prints too many digits (which is certainly better than too few!).

  4. LeeTibbert commented on Feb 28, 2019

    @LeeTibbert

    @ulfjack Thank you for the additional description.

    Java does not have as long a history as C but it still has 25 or so years
    of use "in the wild". To some/most, "long standing existing practice"
    is a useful decision rule for situations where any (probably post-hoc)
    specification is unclear. I did not go all the way back to the earliest
    Java release, but I suspect that it the round trip of 1e-323 would
    yield 1.0E-323.

    I suspect that I an not the only developer who would evaluate Ryu
    for correctness by running (many) test cases of "matched pairs"
    comparing Ryu and Java output. I would find Ryu printing out
    9.9E-324 somewhere between mildly astounding and regrettable.

    I would have to implement a workaround to avoid spending the
    rest of my professional life explaining the difference to the more
    sharp-eyed of my user base.

    With full support for your efforts, I say "Your field, your rules" but
    urge you to consider what I suspect is long standing Java practice.

  5. LeeTibbert commented on Feb 28, 2019

    @LeeTibbert

    Some say that Java is defined by its abundant implementation "quirks".
    Many have grumbled that any attempt to match Java results by a
    rational means or specification is doomed. I for one have plenty
    of scars on my hide from Java quirks.

    If the goal is to have Ryu follow a spec more closely (a laudable goal),
    perhaps a solution would be to paste on a big label saying "RyuDouble.java
    is not Java", similar to the wordplay beloved in some circles "Xinu is not unix"
    and such? Perhaps that is what you are saying already and did not twig
    to it?

    I am probably going to have to take the "Java-like, not one-to-one Java" approach
    myself.

  6. ulfjack commented on Feb 28, 2019

    @ulfjack
    OwnerAuthor

    The discussion thread in upstream is about merging a new implementation that returns 9.9E-324 in this case, as well as a more mathematically precise specification. I tested Ryu against the new implementation and discovered that Ryu returns 1.0E-323 in this case (but otherwise found no differences in output). I don't know if or when upstream will merge a new implementation and whether it will be the one that was discussed.

    In any case, I can clarify in the README that the current behavior differs from Double.toString in some cases.

  7. ulfjack commented on Feb 28, 2019

    @ulfjack
    OwnerAuthor

    There aren't a lot of one-digit and two-digit numbers across all possible double values, so I could check all of them against OpenJDK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    JavaAffects the Java implementation in src/main/java.bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions