-
Notifications
You must be signed in to change notification settings - Fork 12
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
Rounding Options Puzzle #8
Comments
For the compact notation rounding, I was thinking that in the style of the idea of nickelRoundingMagnitude, we could explicitly control the style of compact rounding by keying the options by the magnitude of the compact number. If this is what we have now:
then what I'm thinking might be a new sibling key in that options map:
In that example, we could say that any value with a magnitude less than the lowest key value (-1) takes the options for that lowest key value (magnitude = -1). A similar thing for compact numbers with magnitudes 3, 4, and higher taking the options in the value of the largest key (mag = 2). This functionality is still independent (orthogonal) to the need for |
I think this is minimal set of options that covers all use cases except the distance cutoff case:
In addition, we would make the following changes to the semantics of the existing options. Note: I am using minSig, maxSig, minFrac, and maxFrac instead of the real, longer names, only to make this shorter and easier to read.
I know options 2 and 3 are not very pretty, but I think they get the job done. It's a starting point. Examples// Compact Style 1 (default for notation: "compact")
{
minFrac: 0,
maxFrac: 0,
minSig: 2,
trailingZeros: "strip"
}
// Compact Style 2 (retain trailing zeros)
{
minFrac: 0,
maxFrac: 0,
minSig: 2
}
// Compact Style 3 (more significant digits)
{
minFrac: 0,
maxFrac: 0,
minSig: 3,
trailingZeros: "strip"
}
// Currency Style 1 (default currency style)
{
minFrac: 2,
maxFrac: 2
}
// Currency Style 1 Alternative with trailingZeros: "keep"
{
maxFrac: 2,
trailingZeros: "keep"
}
// Currency Style 2 (strip trailing zeros if they are all zero)
{
minFrac: 2,
maxFrac: 2,
trailingZeros: "stripIfInteger"
}
// Currency Style 3 (nickel rounding)
{
minFrac: 2,
maxFrac: 2,
nickelRounding: true
}
// Currency Style 4 (dime rounding)
{
minFrac: 2,
maxFrac: 1
}
// Distance Style 1 (nearest 50)
{
maxFrac: -1,
nickelRounding: true
} Thoughts? |
Algorithm for resolving minFrac, maxFrac, minSig, and maxSig. I still need to think through the edge cases like 9.9999:
Problem: this algorithm doesn't work for the dime rounding case. :( |
If we could reinvent the existing rounding settings from scratch, I might propose that they be:
If both
We still need to cover the nickel and dime rounding cases. We could shim it in as a more restricted version of ICU's rounding increment setting:
Here's how to express all the rounding styles using these four options: // Compact Style 1 (default for notation: "compact")
{
fractionDigits: 0,
significantDigits: 2,
trailingZeros: "strip",
}
// Compact Style 2 (retain trailing zeros)
{
fractionDigits: 0,
significantDigits: 2,
}
// Compact Style 3 (more significant digits)
{
fractionDigits: 0,
significantDigits: 3,
trailingZeros: "strip",
}
// Currency Style 1 (default currency style)
{
fractionDigits: 2,
}
// Currency Style 2 (strip trailing zeros if they are all zero)
{
fractionDigits: 2,
trailingZeros: "stripIfInteger"
}
// Currency Style 3 (nickel rounding)
{
fractionDigits: 2,
roundingIncrement: 5,
}
// Currency Style 4 (dime rounding)
{
fractionDigits: 2,
roundingIncrement: 10,
}
// Distance Style 1 (nearest 50)
{
fractionDigits: 0,
roundingIncrement: 50
} Great! Okay, so then working backwards, how do we map this onto the existing options? We could say that if both minFrac/maxFrac and minSig/maxSig are present, then we use a slightly modified version of the above algorithm for resolving them:
As it is currently, if a trailing zero is to be retained, it must be "protected" by minFrac or maxFrac. In other words, the default The styles can then be expressed as: // Compact Style 1 (default for notation: "compact")
{
minFrac: 0,
maxFrac: 0,
minSig: 2,
maxSig: 2,
trailingZeros: "strip",
}
// Compact Style 2 (retain trailing zeros)
{
minFrac: 0,
maxFrac: 0,
minSig: 2,
maxSig: 2,
}
// Compact Style 3 (more significant digits)
{
minFrac: 0,
maxFrac: 0,
minSig: 3,
maxSig: 3,
trailingZeros: "strip",
}
// Currency Style 1 (default currency style)
{
minFrac: 2,
maxFrac: 2,
}
// Currency Style 2 (strip trailing zeros if they are all zero)
{
minFrac: 2,
maxFrac: 2,
trailingZeros: "stripIfInteger"
}
// Currency Style 3 (nickel rounding)
{
minFrac: 2,
maxFrac: 2,
roundingIncrement: 5,
}
// Currency Style 4 (dime rounding)
{
minFrac: 2,
maxFrac: 2,
roundingIncrement: 10,
}
// Distance Style 1 (nearest 50)
{
minFrac: 0,
maxFrac: 0,
roundingIncrement: 50
} Thoughts? @ryzokuken @echeran |
In the previous comment, I see the progression of thought from We have:
But for Style 1, the input 1,234,00 after compact rounding becomes 123K, which has 3 significant digits, more than To the comment before that, that lays out 3 changes, in which changes ("options"?) 2 & 3 are admittedly ugly, I still prefer to avoid that if at all possible. I understand the motivation -- simplify the logic / algorithm -- but the tradeoff / cost comes in the mental overhead of mixed semantics. We allow I don't know how to get around the magnitude-dependent way in which the desired formatting behavior seem to be defined (ex: 123,400 -> 123K; 1,234 -> 1.2K). I'd prefer to have that fact just be represented clearly in the user-provided data, which makes it explicit and flexible for the user. |
Right. The behavior is unintuitive in the compact notation case, but it is consistent with the algorithm, which is clean and simple.
Noted. We may be able to get around the mixed semantics if we use the Maybe we could propose the four options (trailingZeros, roundingIncrement, and the new fractionDigits and significantDigits options), and consider [min/max][Frac/Sig] as a historical artifact, which can still be used, but which have odd behavior in some cases. Or maybe we can take the less-clean version of the algorithm for mixing minSig with maxFrac, combined with trailingZeros and roundingIncrement. |
Also noted. The option you suggested makes the magnitude cutoff explicit. We'd still need the roundingIncrement and trailingZeros options in order to cover currency and distance. |
Note to self: I intend to make a little web app to demonstrate the three main approaches listed in this thread. This will help us better understand the limitations of each option. |
Here's my latest attempt. I think it's the cleanest one so far. Keep maximum* talking about rounding magnitude, and minimum* talking about display magnitude (trailing zeros). Then, introduce a mode for both rounding and display magnitude to choose the smaller or the greater of the two conflicting options, called "loose" and "strict" below. I also made an HTML page for playing around a bit, available here. ExamplesmaxFrac = 0, maxSig = 2
minFrac = 1, minSig = 2
maxFrac = 0, maxSig = 2, maxMode = loose
|
Just to make this more concrete: here is my updated set of options:
These options cover maxMode but not minMode from the previous post. I do not think we need to cover minMode at this time because it's not clear to me that there is a compelling use case for that option. |
Bikeshed for the
Bikeshed for the
|
2021-03-11 TC39-TG2 notes: https://github.com/tc39/ecma402/blob/master/meetings/notes-2021-03-11.md#rounding-options-puzzle-8 |
Please fill out this Doodle if you would like to attend a deep-dive to work this out: https://doodle.com/poll/zbf68rcw6k9ztre9?utm_source=poll&utm_medium=link |
2021-04-06: @gibson042 is now aligned with my mental model given the following explanation: So, for instance, if we had the settings
Those settings mean:
Now, consider the number "4.321". The new setting
This resolution algorithm applies separately between the maximum digits settings and the minimum digits settings. So, for example, suppose you had
Consider the input number "1". I will work on additional examples and explanations. But I think we made a whole lot of progress in explaining this model in a way that makes sense. |
I like that explanation, very clear. I suppose that logically there is one
additional possible option. But I don't know that the result would be
something that would find real-world application.
4. roundingPriority: "fractionDigits" means that fraction digits always
wins a conflict.
Mark
…On Tue, Apr 6, 2021 at 2:48 PM Shane F. Carr ***@***.***> wrote:
2021-04-06: @gibson042 <https://github.com/gibson042> is now aligned with
my mental model given the following explanation: maximumFractionDigits
must be interpreted to mean, "round the number at a specific power of 10".
So, for example, "maximumFractionDigits: 2" means "round the number at
10^-2" or equivalently "round the number at the hundredths place".
So, for instance, if we had the settings
{
maximumFractionDigits: 2,
maximumSignificantDigits: 2
}
Those settings mean:
1. Round the number at the hundredths place
2. Round the number after the second significant digit
Now, consider the number "4.321". maximumFractionDigits wants to round at
the hundredths place, producing "4.32". However, maximumSignificantDigits
wants to round after two significant digits, producing "4.3". We therefore
have a conflict.
The new setting roundingPriority offers a hint on how to resolve this
conflict. There are three options:
1. roundingPriority: "significantDigits" means that significant digits
always win a conflict.
2. roundingPriority: "morePrecision" means that the result with more
precision wins a conflict.
3. roundingPriority: "lessPrecision" means that the result with less
precision wins a conflict.
This resolution algorithm applies separately between the maximum digits
settings and the minimum digits settings. So, for example, suppose you had
{
minimumFractionDigits: 2,
minimumSignificantDigits: 2
}
Consider the input number "1". minimumFractionDigits wants to retain
trailing zeros up to the hundredths place, producing "1.00", whereas
minimumSignificantDigits wants to retain only as many as are required to
render two significant digits, producing "1.0". We again have a conflict,
and the conflict is resolved in the same way.
I will work on additional examples and explanations. But I think we made a
whole lot of progress in explaining this model in a way that makes sense.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#8 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACJLEMHZ5535GMKUNDPYIXTTHN6SRANCNFSM4MOWPKNQ>
.
|
I have a puzzle which has perplexed me.
Below, I list real-life use cases for how users want to round their numbers in Intl.NumberFormat. I am trying to figure out some set of options that is capable of expressing these various different rounding strategies.
Compact Notation Rounding
English Descriptions
Thoughts
Style 1 could be expressed as minFrac=0, maxFrac=0, and minSig=2, and when minSig is in conflict with maxFrac, minSig wins, except that we strip trailing zeros. In other words, we could make Style 1 be expressed as:
However, this approach is not capable of expressing Style 2.
We could have an option like "applyFractionGreaterThanIntDigits", which would mean to use minFrac/maxFrac when there are a certain number of integer digits, and minSig/maxSig when there are fewer. This is not a very pretty option, but it is capable of expressing all three styles:
Currency Rounding
English Descriptions
Thoughts
A simple boolean option "stripFractionWhenEmpty" would solve Style 2.
A simple boolean option "nickelRounding" would solve Style 3.
About Trailing Zeros
Note that minFrac already serves absolutely no purpose other than retaining trailing zeros.
Given that minFrac is really only about retaining trailing zeros, for Style 4, we could let minFrac be greater than maxFrac, but it is weird for a minimum to be greater than a maximum.
Since a lot of the problems in this section, as well as Style 2 in the previous section, involve various different ways of treating trailing zeros, maybe we could introduce a "trailingZeroStyle" option, an enum with several different options that encompass all of the use cases.
Distance Rounding
English Descriptions
Thoughts
Style 1 can be represented by a variant of nickelRounding. We could name the option nickelRoundingMagnitude, and if set, it would override fraction and significant rounding. Alternatively, we could allow minFrac/maxFrac to be less than zero, in which case they express the power of 10 at which you round.
Style 2 involves a cutoff. If we can't figure out how to support it, we could declare it out of scope.
Maybe we should throw the minFrac/maxFrac stuff out the door (keep it for backwards compatibility), and devise a whole new way of thinking about rounding strategies.
@echeran @ryzokuken
The text was updated successfully, but these errors were encountered: