Open StephanTLavavej opened 3 years ago
@vitaut https://github.com/microsoft/STL/pull/2157#discussion_r703949775
Great analysis, @StephanTLavavej. All makes sense and even though I'm not a big fan of
1.
it is consistent with my interpretation of the wording and {fmt} should probably do the same in this case.
@miscco https://github.com/microsoft/STL/pull/2157#discussion_r704112114
We could also change the wording to add a trailing zero if the dot is added
Test case and output
Thanks to @statementreply! :heart_eyes_cat:
After PR #2157
libfmt behavior - https://godbolt.org/z/qns4carK7
My analysis
"{:#g}"
and"{:#.6g}"
- unambiguousFor
"{:#g}"
and"{:#.6g}"
, where After PR and libfmt agree that the output is"1.00000"
, I believe that the Standardese is clear. N4892 [format.string.std]/22 passes "6
if precision is not specified" so they are certainly equivalent.to_chars
chars_format::general, 6
wants to trim the zeros and decimal point, but [format.string.std]/6 suppresses both trimmings from happening."{:#.6}"
- wording issueFor
"{:#.6}"
, where After PR and libfmt agree that the output is"1.00000"
, I believe that the Standardese is technically unclear, and should be patched with an LWG Issue. According to [format.string.std]/22, this has a type of "none", but because the precision is specified, we callto_chars
withchars_format::general, 6
. As before,to_chars
wants to trim the zeros and decimal point. [format.string.std]/6 sentence 4 definitely keeps the decimal point. However, [format.string.std]/6 sentence 6 keeps trailing zeros "forg
andG
conversions".:grey_question: The question is, because type-none-with-precision performs a
chars_format::general, 6
conversion, should it be considered ag
conversion?:grey_exclamation: I believe the answer is "yes" - the user's mental model should be "if I pass a precision but not a letter, the letter defaults to
g
and I get all the behavior that I would have gotten if I explicitly saidg
". This is true at theto_chars
layer (as the call is exactlychars_format::general, 6
) and the same should be true for anyformat
post-processing/modified behavior. The fact that libfmt displays this behavior is some additional evidence in favor of this being a reasonable interpretation."{:#}"
- implementation divergenceFor
"{:#}"
, where After PR outputs"1."
, these sentences are even more important. [format.string.std]/22 clearly begins by callingto_chars(first, last, value)
, i.e. this is plain shortest, not general precision. Thus, unlike"{:#.6}"
which (in my opinion above) should be considered an "honorary"g
conversion,"{:#}"
should not be considered ag
conversion. (Plain shortest switches between fixed and scientific with the fewest-characters, tiebreak-prefers-fixed criterion; it does not trim zeros because it doesn't emit them in the first place.) Again, [format.string.std]/6 sentence 4 definitely keeps the decimal point.:question: The question here is, does [format.string.std]/6 sentence 6 apply?
:exclamation: I believe the answer is no: this is not a
g
conversion at theformat
specifier level, this is not achars_format::general
conversion at theto_chars
level, no precision was passed toto_chars
, and no trailing zeros were removed byto_chars
. (There's definitely no "default of 6 precision" involved here, as plain shortest is information-preserving - it may need to emit up to 17 significant digits to round-trip adouble
.)Proposed resolution: :smile_cat:
I believe that this could be clarified in the Standard by changing [format.string.std]/6 sentence 6 from:
to:
Notes
This captures the discussion in https://github.com/microsoft/STL/pull/2157#discussion_r702247363 and https://github.com/microsoft/STL/pull/2157#discussion_r703936220 .