dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.63k stars 4.57k forks source link

To Add an ES6 Compatible Way to ToString double #16471

Open shmao opened 8 years ago

shmao commented 8 years ago

DataContractJsonSerializer converts a double value to a string by calling,

value.ToString("R", NumberFormatInfo.InvariantInfo)

The string is not compatible with ECMAScript 6 (see the specs). For example,

5E-06 should be converted as 0.000005 -3.3333333333333338E-28 should be converted as -3.3333333333333338e-28 4.94065645841247E-324 should be converted as 5e-324

An ES6 compatible way to ToString double and floats would be useful.

JamesNK commented 8 years ago

This would be useful for Json.NET or any other JavaScript/JSON focused .NET library.

AlexGhiondea commented 7 years ago

We would need a formal API proposal for this.

tarekgh commented 7 years ago

if there is any change in the API surface then it will be good if include the proposal of the changes here.

cyberphone commented 6 years ago

Although the spec has not yet been published, Microsoft is (as shown in the document below), actively participating in a standardization effort building on ES6-compatible JSON processing: https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https%3A%2F%2Fraw.githubusercontent.com%2Ferdtman%2FCleartext-JOSE%2Fmaster%2Fdraft-erdtman-jose-cleartext-jws.xml

cyberphone commented 6 years ago

Now the Microsoft sponsored proposal is published: https://tools.ietf.org/id/draft-erdtman-jose-cleartext-jws-00.html This scheme already works in Java, Python, and JavaScript.

ToString("ES") could be an alternative if you feel uncomfortable modifying existing formatters.

tannergooding commented 5 years ago

This is partially resolved by https://github.com/dotnet/coreclr/pull/22040.

-3.3333333333333338E-28 should be converted as -3.3333333333333338e-28

This will now be supported by using ToString("r") (The lowercase R will result in the e being lowercase as well).

4.94065645841247E-324 should be converted as 5e-324

We will now return the "shortest roundtrippable string": 5E-324. As with the above, specifying r will make the e lowercase as well.

5E-06 should be converted as 0.000005

This would require additional changes to the NumberToString function and would require us to consider 0.000005 as more worthwhile than 5E-06 (which is currently the shorter string)

cyberphone commented 5 years ago

@tannergooding If you feel uncomfortable changing NumberToString to support the serialization required by https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02 that's OK because I have already solved this by a V8-inspired port to .NET. However, I have just found a better base in the form of "Ryu" (https://github.com/ulfjack/ryu) which shrinks the code to almost nothing and is very fast as well.

tannergooding commented 5 years ago

If you feel uncomfortable changing NumberToString to support the serialization required by https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02

I think it is something that is reasonable to expose. Although I am unsure if the right thing is to have a new format specifier, a new API, or if we should just fix the existing NumberToString implementation. This likely requires a more formal proposal and discussion.

My initial thoughts are that just fixing the existing NumberToString implementation is the most desirable.

However, I have just found a better base in the form of "Ryu" (https://github.com/ulfjack/ryu) which shrinks the code to almost nothing and is very fast as well.

https://github.com/dotnet/coreclr/issues/19596 is tracking further investigation into the Ryu algorithm. There was just a concern raised was around the additional size requirements.

tannergooding commented 5 years ago

@cyberphone, it might be worth noting that, while steps 6-10 look to be doable (it looks like step 8 is one where we are comparing n against -3, but it wants us to use -6 as the cutoff bounds), step 2 is likely a non-starter.

The ES6 specification looks to specify that If m is +0 or -0, return the String "0". However, this is in direct contradiction to the IEEE 754 specification which specifies that In particular, signs of zeros and infinities are preserved:

The conversions (described in 5.4.2) from supported formats to external character sequences and back that recover the original floating-point representation, shall recover zeros, infinities, and quiet NaNs, as well as non-zero finite numbers. In particular, signs of zeros and infinities are preserved.

tannergooding commented 5 years ago

CC. @danmosemsft as an FYI to the above request.

A summary is that they would like us to either modify our existing NumberToString implementation (or provide some other mechanism to get the desired functionality) to be compatible with the ES6 specification. This would basically involve just tweaking some of the boundaries we are checking that determines when we print using the scientific notation and when we print using the number notation.

cyberphone commented 5 years ago

The ES6 spec for JSON number serialization is indeed a bit deviating from the IEEE spec but in a transport format it does in my opinion not make (too) much sense sending "-0", NaN, or Infinity. The latter are BTW illegal in JSON and in my .NET and Java implementations throw exceptions. That is, numbers with the pattern 7ffxxxxxxxxxxxxx cannot be serialized. ES6/JSON serializations like -0.0000033333333333333333 and 999999999999999700000 also defy "normal" rules.

That is, if I were to update ToString I would due to these differences add a new identifier to not have to make sub-optimal compromises.

Java version based on Ryu: https://github.com/cyberphone/json-canonicalization/blob/master/java/canonicalizer/src/org/webpki/jcs/NumberToJSON.java

ghost commented 4 years ago

Tagging subscribers to this area: @tannergooding Notify danmosemsft if you want to be subscribed.

cyberphone commented 4 years ago

An RFC building on this scheme will be published in the coming weeks: https://www.rfc-editor.org/authors/rfc8785.html