-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
-0.0 should format with a minus sign by default #1074
Comments
I feel pretty strongly about this and believe we should not display sign for minus zero for
I filled rust-lang/rust#24623 |
I'm not too hung up on which is the default behaviour, but I'd at least want to have a way to print a float such that it look like a true real number, ie. never display a sign for +/-0. Generally, if I see +0 or -0 in a UI, I'm going to think that it's actually just a very small number which is zero when rounded but actually does have a sign, whereas with no sign it's obvious that it's actually zero.. |
I'm fine with adding some way to round -0 up to +0 during formatting. It's not quite reasonable to expect We also implement Python, Ruby, and Racket each have a Issue #24624 illustrates the problem with using |
I think somebody misrepresented what It has nothing to do with end users whatsoever. Or at least didn’t when it was still called |
I suppose the term is "user-facing" rather than "end-user". It's from RFC 565. It also includes this bit:
|
I like this idea but |
FWIW, Swift has a distinction between My initial reaction to this was a knee-jerk "no it shouldn't!" but I think it actually should. @nagisa is correct, An alternative would be to use the |
I don't have a strong opinion here, but I largely agree with @kballard's analysis: you should probably be using an adapter or modifier or some other facility to tailor the output of things like floating point numbers if you're going to display them directly to a user. |
I also have pretty few strong opinions in this area. |
My feeling is that |
Therefore, I think it's helpful to point out negative zeros as clearly as possible. I'd rather have a small number of people slightly confused or amused by the "meaningless" sign than a small number of people waste hours chasing a stupid bug caused by a negative zero. It doesn't even matter if they know that there is such a thing as negative zero, even an IEEE-754 expert can be mislead if their debugging output indicates positive zero. Aside: If |
I share @rkruppe's view. This would be a hindrance to doing numerical / scientific computing in Rust. I would prefer there to be an option to suppress printing |
Maybe we can add |
What's the status of this issue? I ran into it when parsing a file containing negative zeros (as I couldn't find a formatting parameter that would give me the desired output (include minus but with no decimal point) so I have to wrap my floats into a struct when printing and implement custom Display. The docs say Realistically, can the behavior of |
That feels quite wrong. The documentation of Case in point: I have myself reported a bug in an SVG-reading library failing in locales where the comma is the fraction-part separator; as it turns out, it was because for parsing path definitions it used |
This kind of bug is exactly the reason why we shouldn’t have implicitly-locale-dependent functions. As to U+2212 MINUS SIGN, it’s not really helping anybody. Users might copy/paste that output into other software that knows to parse U+002D but not U+2212. |
The thing is, one can argue
Sure, but that is a ‘mere’ quality-of-implementation issue, not a correctness issue. Even when sticking with European digits, one could legitimately make The real point though is that |
The module-level
It's certainly possible that an alternative version of Rust might choose to make changes to the |
IEEE754-2019 states that transformations from a floating point to an external decimal character sequence and back shall (are required to) preserve signs for infinites and zeros, and this was likely specified also in previous versions (2008) but I have not extensively diffed it. But as a result, I do not really see a reason to debate here. |
@workingjubilee that's the case for Did anyone ever guarantee that |
No, in fact while
|
And the log file is exactly the concern. IEEE754 makes it pretty clear that 0.0 and -0.0 are not to be treated interchangeably as data points and should round-trip with sign, and so a logged value by default should be capable of being turned into its source float pattern. It is also confounding that we say that the negative sign is printed by default when here it is not: https://doc.rust-lang.org/stable/std/fmt/index.html Though it also mentions the Signed trait which... doesn't exist in Rust at the moment, I believe? So that's clearly in dire need of updating either way. Display should certainly output formatted data in a way that is presentable to an ordinary human being instead of a cybernetically attached headcrab, but I do not think it should unnecessarily assume a presence or absence of technical (or mathematical!) capability in doing so. Either way, |
I just did a quick test of the basic printing operation on the literal
(note that Perl will use libc if you write Most languages seem to be preserving the sign. I think it's pretty reasonable for Rust to follow the majority behavior here and preserve it too. |
Is there the distinction of something like |
Every example there is basically Debug output, and if you wanted alternative formatting you'd do it yourself. Most languages don't have a Debug/Display distinction. |
Swift does. But I don't believe that reasoning based on the idea that there should be a strong divide between Debug and Display is very applicable here even if we were to presume such was a good place to start. That intended divide is why Rust does not provide derivable implementations for Display, forcing the programmer to think carefully about choosing a presentation. Thus people will write Having done that, we're patting ourselves on the back a bit much if we are drawing a bright line between "write different format chars in Rust" from "write different format chars in C". And in C they are consistently conformant in this regard to IEEE754. But as nagisa stated earlier, the question for std implementations of Display are whether it has "an obvious, widely accepted string representation" and we've established strongly that there is a widely accepted convention here: a technical standard and majority compliance with it. And I did go back further and found that IEEE854-1987 is explicit about "-0 should print as -0" and IEEE754-1985 is suggestive of it, so this is not a recent change in 2019 or even 2008. |
Add IEEE 754 compliant fmt/parse of -0, infinity, NaN This pull request improves the Rust float formatting/parsing libraries to comply with IEEE 754's formatting expectations around certain special values, namely signed zero, the infinities, and NaN. It also adds IEEE 754 compliance tests that, while less stringent in certain places than many of the existing flt2dec/dec2flt capability tests, are intended to serve as the beginning of a roadmap to future compliance with the standard. Some relevant documentation is also adjusted with clarifying remarks. This PR follows from discussion in rust-lang/rfcs#1074, and closes rust-lang#24623. The most controversial change here is likely to be that -0 is now printed as -0. Allow me to explain: While there appears to be community support for an opt-in toggle of printing floats as if they exist in the naively expected domain of numbers, i.e. not the extended reals (where floats live), IEEE 754-2019 is clear that a float converted to a string should be capable of being transformed into the original floating point bit-pattern when it satisfies certain conditions (namely, when it is an actual numeric value i.e. not a NaN and the original and destination float width are the same). -0 is given special attention here as a value that should have its sign preserved. In addition, the vast majority of other programming languages not only output `-0` but output `-0.0` here. While IEEE 754 offers a broad leeway in how to handle producing what it calls a "decimal character sequence", it is clear that the operations a language provides should be capable of round tripping, and it is confusing to advertise the f32 and f64 types as binary32 and binary64 yet have the most basic way of producing a string and then reading it back into a floating point number be non-conformant with the standard. Further, existing documentation suggested that e.g. -0 would be printed with -0 regardless of the presence of the `+` fmt character, but it prints "+0" instead if given such (which was what led to the opening of rust-lang#24623). There are other parsing and formatting issues for floating point numbers which prevent Rust from complying with the standard, as well as other well-documented challenges on the arithmetic level, but I hope that this can be the beginning of motion towards solving those challenges.
Rust nightly 4/13 allows f64::parse to handle "infinity", case insensitive. This broke cases such as `Number("Infinity")`, which should return `NaN` in AVM1. Additionally, Rust will now print "-0" for negative zero, when previously it would print "0". * Return NaN for inf cases ("inf", "-Infinity", "+INF", etc.) * Add a test for `Number("inf")` (this was also incorrect before the latest nightly) * Add a special case for zero in `f64_to_string` to ensure that -0.0 gets coerced to "0". For more info, see: rust-lang/rfcs#1074
Rust nightly 4/13 allows f64::parse to handle "infinity", case insensitive. This broke cases such as `Number("Infinity")`, which should return `NaN` in AVM1. Additionally, Rust will now print "-0" for negative zero, when previously it would print "0". * Return NaN for inf cases ("inf", "-Infinity", "+INF", etc.) * Add a test for `Number("inf")` (this was also incorrect before the latest nightly) * Add a special case for zero in `f64_to_string` to ensure that -0.0 gets coerced to "0". For more info, see: rust-lang/rfcs#1074
As of rustc Both Perhaps this could be closed now? |
For those following along, this was fixed in rust-lang/rust#78618 in version 1.53.0. The release notes said
Referencing |
Also, apparently updating Rust fixes my last test! Can't commit that change but w/e, I'm happy anyways :) rust-lang/rfcs#1074
I think negative zero should be formatted with the minus sign preserved by default.
Issue 20596
Issue #20596 restored the sign for
Debug
but not forDisplay
, because "for users, negative zero makes no sense." I feel this is a poor rationale:[+-]Infinity
andNaN
.to_string
is Rust's canonical string representation. It is used to serialize a float for JSON.{:10}
is used to serialize floats for CSV output.The issue indicated that the CSS serializer could instead use
Debug
. There are problems with usingDebug
for FP serialization:Debug
is inflexible. It should be fixed per issue #24556 to use minimal sufficient precision, but searching for this representation is non-trivial. For efficiency, familiarity, or interoperability, programs might want the exponent form{:1.8e}
for serialization (for f32). This format mirrors the%1.8e
format in C and other languages, but whereas C round-trips every value, Rust round-trips every value but minus zero.Debug
is for debugging. Serialization formats need to be stable and documented. We would need to documentDebug
's output and guarantee its backward compatibility.Display
andto_string
are the default conversion APIs, so if the default suppresses-0
, libraries will take this as a cue that the Rust ecosystem ought to suppress-0
. In other words, it is unclear that the existing JSON and CSV serializers will change toDebug
. Perhaps this is the intent; it's unclear. Outside Rust, the JSON serializers I've tested for Python, Ruby, Java (gson), and Go all output minus zero.It is surprising that
{:+}
prints+0
, while{:+?}
prints-0
for the same number. They appear to contradict.Other languages
Hiding minus zero makes Rust inconsistent with many other languages. Languages that show it include: C, C++, Go, Haskell, Java, Lua, OCaml, Perl, Python, Racket, and Ruby. As a systems language, it makes sense to do the same thing as C/C++, but even common Unix scripting languages show the value.
Go and Perl: the default
print
hides minus zero, whereas Perl'sprintf
and Go'sfmt
package show it. Go'sprint
outputs floats using the simple format[+-]N.NNNNNNe[+-]NNN
; it's not trying to be user-friendly or sufficiently precise.JavaScript hides minus zero, but it has no separate integer type. If a language must choose between preserving the illusion of an integer type or representing IEEE-754 precisely, it's reasonable to choose the former. Rust, on the other hand, has FP types. Moreover, even JavaScript's choice isn't obvious—Lua lacks integer types but still prints
-0
.C# hides the minus sign.
Neither JavaScript nor C# have a built-in
printf
-style interface, nor do they display -0 with a+
sign.cc @rkruppe @lifthrasiir @aturon @Diggsey @SimonSapin
The text was updated successfully, but these errors were encountered: