Closed sffc closed 3 years ago
Is halfUp
the same as "round to even"?
Is
halfUp
the same as "round to even"?
I think that would be halfEven
. See the chart: http://userguide.icu-project.org/formatparse/numbers/rounding-modes
FWIW Java BigDecimal has basically the same set of modes, plus an UNNECESSARY mode which throws if rounding was needed. I don't know whether this was an independent invention, though, given that both of these projects had strong IBM involvement/authorship.
I suspect most of these are needed. The only two I'm suspicious of are floor and ceiling; I wonder if up and down would be enough (or, if floor/ceiling would be enough, without up/down). I'd like to collect more data somehow.
I tried to ask about rounding modes in the decimal survey but I haven't gotten any answers yet that really got at the semantics needed for negative numbers. I suspect negative numbers are important in some accounting/money use cases, though. Maybe this is something to follow up on in further interviews.
Is
halfUp
the same as "round to even"?
No. halfUp
will round ±0.5 to ±1, while halfEven
will round ±0.5 to ±0.
The default internal IEEE arithmetic rounding is halfEven
. The default rounding the IRS asks you to do on taxes is halfUp
.
The set of modes here seems like one of the standard rounding repertoires and seems fine to me.
We should align the Intl rounding modes with the Decimal rounding modes. Perhaps this is a decision best made in the Decimal proposal, and the roundingMode option for 402 could piggy-back on that proposal instead of being in this proposal.
@sffc: The Decimal rounding modes are the same as the various IEEE binary arithmetic rounding modes. They're part of the same standard and all have their uses.
Great, seems like we all agree that these two proposals should be aligned. (I agree that IEEE 754 is a good guide for rounding modes, but one possibility is that we could decide on a subset.)
Some bikeshedding feedback on rounding mode names:
The only two I'm suspicious of are floor and ceiling; I wonder if up and down would be enough (or, if floor/ceiling would be enough, without up/down).
IMHO, the names "floor" vs. "down" and "ceiling" vs. "up" may confusingly similar for developers who may not be familiar with IEEE 754 or other platforms' rounding modes. Using all 4 terms may invite confusion.
My suggestion would be to align naming with existing methods used in JS's Math
object. By using "trunc" instead of "down", it'd remove the ambiguity noted above. It's also probably better for JS developers if they can leverage existing knowledge of Math
instead of having to learn a new (or slightly different) set of terms depending on which JS methods are being used. IMHO this is more important than aligning names to ICU or Java which will be less familiar to JS developers.
So:
ciel
(not "cieling")floor
trunc
(not "down")Math.round
doesn't actually use any of the ICU modes-- it rounds -1.5 to -1, but 1.5 to 2. So if we had to break naming compatibility, this is probably the place. Perhaps using a prefix of "round" so developers will know that all of these are variations of Math.round
-like behavior?
roundHalfCiel
or perhaps just round
- matches Math.round()
roundHalfEven
- banker's rounding / IEEE defaultroundHalfTrunc
- this is ICU's "Half Down"We should align the Intl rounding modes with the Decimal rounding modes.
I agree strongly that the same rounding modes and names should be used everywhere: Intl, Decimal, and Temporal.
One exception to alignment might be the default rounding mode used. For example, I could see Decimal's default being roundHalfCiel
(because AFAIK that's the expected default for the main use case for Decimal which is financial calculations) while Temporal's default could be trunc
(IMHO it would be weird to default to rounding half-days up to a full day without users opting into that behavior).
@gibson042 also suggested looking at the IEEE 754 list of rounding modes:
https://en.wikipedia.org/wiki/Floating-point_arithmetic#Rounding_modes
There are already many behaviors in ECMA 262, and I believe each of them should be explicitly specifiable:
Math.ceil
"rounds" towards +∞ (ICU "Ceiling").Math.floor
"rounds" towards -∞ (ICU "Floor").Math.trunc
and abstract operation ToInteger "round" towards zero (ICU "Down").Math.round
rounds ties towards +∞ (apparently not in ICU).Math.fround
and internal conversions from mathematical values round ties to even, but ties relate to differences between arbitrary Number values rather than just integers (ICU "Half Even").I find the ICU names confusing, and would not recommend using them; @justingrant's suggestions are better. If we include ICU "Up" then I would propose "awayFromZero", and likewise "halfAwayFromZero" or "tiesAwayFromZero" for ICU "Half Up".
Wikipedia covers even more round options https://en.wikipedia.org/wiki/Rounding
Up
and Down
is ambiguous: i.e. is the value
or absolute value
higher or lower
To explain ceil
or floor
, one typically uses round up
or round down
respectively (thus are synonymous).
I agree that the ICU usage for "Up" and "Down" is uncommon.
I like the recommendation for awayFromZero
and possibly towardsZero
.
For consideration, CSS's round()
function chose the values nearest
, up
, down
, and to-zero
. (We leave open the possibility of adding away-from-zero
later, but as it apparently wasn't useful enough to warrant a dedicated function in JS, we left it out for now.)
We haven't added finer modes for disambiguating nearest
at half-integers (currently we always choose the up
value), but the grammar is compatible with adding such switches later.
We purposely avoided floor/ceiling because ceiling is hard to spell, and we already had precedent for a similar feature in line layout using the up/down keywords.
(Having CSS's choices flow back into JS, when JS's choices originally informed much of them in this case, would be pretty funny to me.)
Kudos on that naming, it seems sensible to equate "up"
with "towards +∞" and "down"
with "towards -∞", and having "nearest"
resolve ties "up" matches lay intuition at least for positive numbers (even if it is mathematically biased). I would not object to adopting all four, preferably also adding a "round ties to even" mode with whatever name seems most likely for CSS.
I also like CSS naming, but for a JS API I think that matching existing naming elsewhere in JS is probably better than having two different names for the same operations between Intl
/Temporal
vs. Math
.
Okay, so for Intl.NumberFormat, we are currently using "half away from zero" as the default and only rounding mode, which is not available in either 262 or in CSS. So, we need to include that option. How about this:
"auto"
= round half away from zero
"nearest"
= round half toward +∞
"ceil"
= round all toward +∞
"floor"
= round all toward -∞
"trunc"
= round all toward zero
"even"
= round half toward an even number
"floating"
= convert to IEEE 32-bit precision and then round toward an even number
I don't know if we need "even"
or "floating"
, but the behavior would be:
Number | maxFrac | Rounding Mode | IEEE 32-bit Float | Result | Comments |
---|---|---|---|---|---|
1.245 | 2 | even | N/A | 1.24 | |
1.245 | 2 | floating | 1.2450000047683716 | 1.25 | 1.25 is odd, but closer to IEEE |
1.25 | 1 | even | N/A | 1.2 | |
1.25 | 1 | floating | 1.25 (exact) | 1.2 | exact midpoint in IEEE; round even |
See also https://github.com/tc39/ecma402/issues/128 for more discussion on "floating"
.
In writing Temporal sample code recently, the lack of a "round all away from zero" mode has been problematic, because it means you'll need to conditionally change the rounding mode between ceil
and floor
depending on the sign of value. It'd be good to add this rounding mode too.
From discussion with @gibson042 and @justingrant:
And the tiebreaking modes:
This should cover the complete set.
Additional conclusions:
I opened a Twitter poll for the naming of the "away from 0" option:
https://twitter.com/_sffc/status/1319715712513019905
So far "expand" is the most popular of the options presented, but "Other (please reply)" is currently winning the poll, with the most likes going to "RoundAwayFromZero". This would make the list:
roundingMode:
We could also choose to go all-in with the verbose names and do
roundingMode:
I still prefer introducing a new short word, because I believe people will learn what it means, and we set a precedent for others to follow. However, I will bring this to the TC39 research group for further advice.
CC @mpcsh
This topic should also be concerned about Number.prototype.toFixed
:
(1.02).toFixed(1); // => "1.0"
(1.05).toFixed(1); // => "1.1" round by default
// If we can specify types:
(1.05).toFixed(1, 'floor'); // => "1.0"
(1.02).toFixed(1, 'ceiling'); // => "1.1"
@aleen42 , The Number#toFixed works a little differently now, although, it can be changed as well:
(1.05).toFixed(17)
// right now it returns "1.05000000000000004" because 1.05 == 1.0500000000000000444089209850062616169452667236328125 for "Number value"
I really like the "expand" name, and I think that what was discussed and decided above is a reasonable conclusion; I can live with it for application in other proposals like Temporal and Decimal as well.
My only concern here is that the six "half" modes are a bit overkill. It's nice to have the whole logical space thought out, but I'm wondering what will actually be useful. For example, do we have use cases in mind for halfOdd
?
Some costs to consider from including so many modes:
These costs are not necessarily too high, but it makes me prefer that we have clear motivation in mind for the modes that we add, rather than just adding all logically possible modes frrom the start.
ICU (and, by extension, the Java SDK), ECMA-262, and CSS all support different sets. The sets we could take are:
Having implemented rounding modes in ICU, the marginal cost of filling in the set of 10 is extremely minimal relative to either of the two more restrictive sets. Given that the implementation cost is minimal, I find it clearer if we just have the full set, rather than omitting some arbitrary subset based on precedent.
Note: The only rounding mode not being proposed is "floating"
, available in 262 and CSS but not ICU. Because ICU must convert doubles to strings in order to resolve things like compact and scientific notation, implementing "floating"
in ICU would have an additional cost.
I share Dan's opinion regarding the number of tiebreak modes we have. Particularly, it's strange to me both halfEven
and halfOdd
. There's real application for halfEven
, which justifies the inclusion of such mode if we decide to do so, but I'm curious to know about any usage of halfOdd
.
2021-04-06: after discussion with @littledan, @caiolima, @ryzokuken, and @gibson042, we are now recommending the following 9 modes (all except halfOdd):
ceil floor expand trunc halfCeil halfFloor halfExpand halfTrunc halfEven
Note on the decision to recommend camel case: https://github.com/tc39/ecma402/blob/master/docs/style-guide.md#casing-conventions
I'm late to the party, but I wanted to suggest two alternative names for the "away from 0" rounding mode:
I also like "expand", but it is a tad misleading. Rounding a negative number to a smaller value (e.g. -1.1 → -2) doesn't sound like expanding to me unless you're defining the expansion as being made toward the absolute value
Update: I just saw that in the Mozilla docs, they say that negative values round "more negative". This is an okay-ish framing, but I still like "spread" and "straddle" better.
ICU has the following options:
"halfUp"
(default)"halfEven"
"halfDown"
"ceiling"
"floor"
"up"
"down"
Chart: http://userguide.icu-project.org/formatparse/numbers/rounding-modes
Do we want to include all those in Intl.NumberFormat? How would we pick a subset?
@waldemarhorwat @littledan