1

The documentation recommends that I use G17 rather than R, as R can sometimes fail to round-trip.

However, (1.0/10).ToString("G17") gives "0.10000000000000001", which is pretty horrible. while the round-trip format seems to work just fine (and gives "0.1"). I'm happy to spend a few cpu cycles in order to get a more aesthetic result. But potential round-trip failures are more concerning.

For what sort of (Double) values does R fail to round-trip? And how badly? Does the .Net version (we run on both Net Framework 4.72 and NetCore 3.1) affect things? Could writing on one platform and reading on another make round-trip failure more frequent?

We are considering writing doubles to R first, parsing to check the round-trip, and falling back to G17 only if that fails. Is there a better way to get nicely formatted, reliable results?

Rob
  • 4,327
  • 6
  • 29
  • 55
  • 1
    _"which is pretty horrible"_ ... it's a floating point type. What do you expect? What would you consider to be a "aesthetic" result? – Fildor Jul 23 '21 at 07:06
  • As in the example. I realise the result of `1.0/10` cannot be expressed exactly as a floating point number, but when formatted with the round-trip specifier, this result is shown as `"0.1"` which is exactly what I want. – Rob Jul 23 '21 at 07:21
  • 1
    Ok, so Roundtrip is what you actually want, just you are afraid because of that "could fail" remark, is that correct? – Fildor Jul 23 '21 at 07:22
  • _"To work around the problem of Double values formatted with the "R" standard numeric format string not successfully round-tripping **if compiled using the /platform:x64 or /platform:anycpu switches and run on 64-bit systems**"_ - does this apply to you? – Fildor Jul 23 '21 at 07:27
  • Yes, we run on 64 bit for the most part. – Rob Jul 23 '21 at 07:43
  • 1
    The documentation is insufficient to answer this question. While it says that the “R” format sometimes fails to round-trip (in spite of that being its purpose), it does not state why. Without that information, we cannot know for which case(s) it fails to round-trip, so we cannot design a workaround, and the documentation does not list an alternative that would round-trip while formatting nicely. – Eric Postpischil Jul 23 '21 at 11:30
  • @chux-ReinstateMonica I tried `-0.0` quickly in Linqpad 6 and it seems to round-trip correctly. – Rob Jul 23 '21 at 17:34
  • Rob, Well it was some time ago.... Good to know -0.0 works . – chux - Reinstate Monica Jul 23 '21 at 19:00
  • Might have been wrong about `-0.0` - C# seems to think it's equal to `0`, so round-tripping doesn't really come into it. – Rob Jul 27 '21 at 15:07

1 Answers1

1

Round trip here is a numeric value that is converted to a string is parsed back into the same numeric value.

OP's dismay with (1.0/10).ToString("G17") gives "0.10000000000000001", which is pretty horrible. is an incorrect assessment of round trip success. The intermediate string is only half of the round trip.

Double exactly encodes about 264 different values. All encodable values are some limited integer * 2some_power. 0.1 is not one of them. 1.0/10 makes a math quotient of 0.1, but a slightly different Double value. The closest Double value and it two closet Double neighbors:

 Before     0.099999999999999991673... 
            0.100000000000000005551...
 After      0.100000000000000019428...
 OP report  0.10000000000000001
 Digit count  12345678901234567

OP's example should then be <(0.100000000000000005551).ToString("G17") gives "0.10000000000000001"> which is good.

Printing a Double with G17 provides 17 significant digits, enough to successfully round trip.


For what sort of (Double) values does R fail to round-trip? And how badly?

For this, I go on memory. R sometimes used less than 17 significant digits, like 15, to form the intermediate string. The algorithm used to determine the digit count sometimes came up a bit short and hence "some cases fails to successfully round-trip the original value".

Using G17 always works. For some values, less than 17 also would have worked. The down-side to G17 is exactly in cases like this. Fewer than 17 digits would have worked and have provided a more pleasing, shorter intermediate string.

A pleasing human readable string is not the goal of round-tripping. The goal is to form the same Double after going form value to string to value, even if the intermediate string has extra digits in select cases.


Is there a better way to get nicely formatted, reliable results?

"nicely formatted" is an additional burden to round trip. MS attempted to do so with R and failed in some cases, preferring to retain the same broken functionality than to fix it.

OP would be wise to avoid that path and forego the goal of nicely formatted intermediate string and focus on the round-trip goal of getting the final same value back.

Use G17.


We are considering writing doubles to R first, parsing to check the round-trip, and falling back to G17 only if that fails.

That would work if done correctly. To assess correctness, test your code with many values and also post it and the test harness for code review.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I accept RoundTrip means a string format that can be parsed to exactly the same double. The point is, there are many strings that parse to give the same double value, and some are nicer than others. I don't doubt that `G17` is the simplest (and hence least likely to have bugs) implementation that round-trips, but I'd prefer a working `R` format, or even something cool like [Ryu](https://stackoverflow.com/a/57050587/738851). – Rob Jul 27 '21 at 15:04
  • @Rob I, too, would like a _working_ R format. I see no fast and certain solution. For "nice" intermediate string, I would recommend a C like solution using `"%.*g", prec, x`, first with `prec = 17` and then `prec--` until the round-trip fails. Good luck. – chux - Reinstate Monica Jul 27 '21 at 15:22