Skip to content
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

What rounding modes to include? #7

Closed
sffc opened this issue Apr 23, 2020 · 27 comments
Closed

What rounding modes to include? #7

sffc opened this issue Apr 23, 2020 · 27 comments
Labels
has-consensus Has consensus and ready to implement

Comments

@sffc
Copy link
Collaborator

sffc commented Apr 23, 2020

ICU has the following options:

  1. "halfUp" (default)
  2. "halfEven"
  3. "halfDown"
  4. "ceiling"
  5. "floor"
  6. "up"
  7. "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

@erights
Copy link

erights commented Apr 23, 2020

Is halfUp the same as "round to even"?

@sffc
Copy link
Collaborator Author

sffc commented Apr 23, 2020

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

@littledan
Copy link
Member

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.

@waldemarhorwat
Copy link

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.

@sffc
Copy link
Collaborator Author

sffc commented Apr 23, 2020

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.

@waldemarhorwat
Copy link

@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.

@littledan
Copy link
Member

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.)

@justingrant
Copy link

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")
  • "round" is the difficult case because 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 default
    • roundHalfTrunc - this is ICU's "Half Down"
  • I'm not sure "up" is an important enough use case to justify the possible confusion between whatever we call it and "ciel". But if someone can think of a non-confusing name, it'd be OK.

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).

@sffc
Copy link
Collaborator Author

sffc commented Jul 31, 2020

@gibson042 also suggested looking at the IEEE 754 list of rounding modes:

https://en.wikipedia.org/wiki/Floating-point_arithmetic#Rounding_modes

@gibson042
Copy link
Contributor

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".

@chyipin
Copy link

chyipin commented Aug 1, 2020

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.

@tabatkins
Copy link

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.)

@gibson042
Copy link
Contributor

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.

@justingrant
Copy link

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.

@sffc
Copy link
Collaborator Author

sffc commented Oct 8, 2020

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:

  • Intl.NumberFormat roundingMode:
    • "auto" = round half away from zero
      • not in CSS
      • not in 262 Math
      • ICU HALF_UP
    • "nearest" = round half toward +∞
      • CSS "nearest"
      • 262 Math.round
      • not in ICU, but would probably be called HALF_CEILING (not yet proposed)
    • "ceil" = round all toward +∞
      • CSS "up"
      • 262 Math.ceil
      • ICU CEILING
    • "floor" = round all toward -∞
      • CSS "down"
      • 262 Math.floor
      • ICU FLOOR
    • "trunc" = round all toward zero
      • CSS "to-zero"
      • 262 Math.trunc
      • ICU DOWN
    • OPTIONAL: "even" = round half toward an even number
      • not in CSS
      • not in 262
      • ICU HALF_EVEN
    • OPTIONAL: "floating" = convert to IEEE 32-bit precision and then round toward an even number
      • not in CSS
      • 262 Math.fround
      • Not in ICU

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 tc39/ecma402#128 for more discussion on "floating".

@sffc sffc added the discuss Needs discussion to make progress label Oct 8, 2020
@justingrant
Copy link

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.

@sffc
Copy link
Collaborator Author

sffc commented Oct 23, 2020

From discussion with @gibson042 and @justingrant:

  • ceil (toward +∞)
  • floor (toward -∞)
  • expand (away from 0)
  • trunc (toward 0)

And the tiebreaking modes:

  • halfCeil
  • halfFloor
  • halfExpand
  • halfTrunc
  • halfEven
  • halfOdd

This should cover the complete set.

Additional conclusions:

  1. We expect the plan of action to adopt camel case.
  2. We propose halfExpand as the default.
  3. We could consider using "tie" instead of "half", but we currently plan to move forward with "half".

@sffc
Copy link
Collaborator Author

sffc commented Oct 24, 2020

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:

  • ceil
  • floor
  • awayFromZero
  • trunc
  • halfCeil
  • halfFloor
  • halfAwayFromZero
  • halfTrunc
  • halfEven
  • halfOdd

We could also choose to go all-in with the verbose names and do

roundingMode:

  • towardPositiveInfinity
  • towardNegativeInfinity
  • awayFromZero
  • towardZero
  • halfTowardPositiveInfinity
  • halfTowardNegativeInfinity
  • halfAwayFromZero
  • halfTowardZero
  • halfEven
  • halfOdd

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

@sffc
Copy link
Collaborator Author

sffc commented Feb 11, 2021

2021-02-11: Go with #7 (comment).

@sffc sffc added has-consensus Has consensus and ready to implement and removed discuss Needs discussion to make progress labels Feb 12, 2021
@sffc
Copy link
Collaborator Author

sffc commented Feb 12, 2021

@aleen42
Copy link

aleen42 commented Feb 23, 2021

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"

@Yaffle
Copy link

Yaffle commented Feb 23, 2021

@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"

@littledan
Copy link
Member

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:

  • There's a lot of documentation to read through about signs and rounding when you're trying to figure out which mode to use. (This was confusing to me when I was trying to learn about Java's BigDecimal rounding, and has made me think that maybe some aren't necessary.)
  • There is more to implement in engines
  • There is more to understand when reading and editing code, to figure out what the rounding mode is doing and how significant it is to the program's functioning (especially since often, multiple modes are OK and provide good results)

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.

@sffc
Copy link
Collaborator Author

sffc commented Mar 9, 2021

ICU (and, by extension, the Java SDK), ECMA-262, and CSS all support different sets. The sets we could take are:

  1. All 10 modes.
  2. The union of ECMA-262 and CSS, plus the current ECMA-402 behavior, which gives 6 modes (remove "halfFloor", "halfEven", "halfOdd", and "expand").
  3. The union of ECMA-262, CSS, and ICU, which gives 8 modes (remove "halfFloor" and "halfOdd").

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.

@sffc sffc added discuss Needs discussion to make progress and removed has-consensus Has consensus and ready to implement labels Mar 9, 2021
@caiolima
Copy link

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.

@sffc
Copy link
Collaborator Author

sffc commented Apr 6, 2021

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

@sffc sffc added has-consensus Has consensus and ready to implement and removed discuss Needs discussion to make progress labels Apr 18, 2021
@sffc sffc closed this as completed in a0c5b85 Apr 23, 2021
@PaulRBerg
Copy link

PaulRBerg commented Apr 16, 2023

I'm late to the party, but I wanted to suggest two alternative names for the "away from 0" rounding mode:

  • spread (and halfSpread)
  • straddle (and halfStraddle)

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has-consensus Has consensus and ready to implement
Projects
None yet
Development

No branches or pull requests