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

Add :percent #988

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Add :percent #988

wants to merge 1 commit into from

Conversation

eemeli
Copy link
Collaborator

@eemeli eemeli commented Jan 21, 2025

Fixes #956

A separate :percent function is added, and the style option dropped from :number and :integer.

As proposed in #987, selection is not enabled for :percent.

I've also left out the notation and compactDisplay options, as I'm not aware of any actual use cases combining them with percent formatting.

@eemeli eemeli requested review from aphillips and sffc January 21, 2025 22:22
Comment on lines +502 to +503
For the purposes of formatting,
the numeric value of the _operand_ is multiplied by 100.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have a separate function, purpose built for it, should we offer a pre-computed mode?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean an option toggling if the input is multiplied or not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ICU4C calls it "scale" or "multiplier"

ICU4X calls it "multiply_pow10"

I don't think we've had a proper bikeshedding session on this before. I've sort-of named it what I felt like naming it and no one ever pushed back with better names.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we'll be defaulting to a value 1 getting formatted as something like "100%", I think scale makes the most sense, with a default value of 1 and supporting 100 as an alternative. With this approach,

{42 :percent scale=100}

would then be formatted as "42%".

Or is the ICU4C meaning of this option the exact opposite?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thought: an enumeration might be appropriate here. Something like:

  1. scalingMode: "scale" scales the number: 0.5 => 50%
  2. scalingMode: "passThrough" passes through the number: 50 => 50%

(sorry for the quad-post)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, scale is a rather sub-optimal choice, as it demonstrably can be understood as either "scale the number by this much" or "the scale extends to this value as the maximum".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More suggestions, with some help from an LLM

enum PercentageScale { Fraction, Percentage }
enum InputMode { Decimal, Scaled }
enum ValueType { Normalized, Preformatted }
enum PercentMode { Ratio, Percent }
enum ScaleType { UnitInterval, WholePercent }
enum InputScale { PartsPerHundred, FinalPercent }
enum Scale { Raw, Scaled }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe style=raw, as an alternative to a default style=scaled?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to avoid any option with the unqualified style name.

The _resolved value_ of an _expression_ with a `:percent` _function_
contains an implementation-defined numerical value
of the _operand_ of the annotated _expression_ multiplied by 100,
together with the resolved options' values.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity, and since "resolved value" and "options" are the terms, perhaps:

Suggested change
together with the resolved options' values.
together with the _resolved values_ of the _options_.

Copy link
Collaborator Author

@eemeli eemeli Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variants of this same snippet are used in nearly all the functions. This change would be better made separately, affecting all of them at once.

But yes, referring to the options' resolved values would be appropriate, but note that this isn't the same as options, given that the resolved options are determined by the function handler. In practice they can include ones coming from the operand as well, and don't necessarily contain all of the options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps "resolved values of the resolved list of options"? Oh the pedantry...

Comment on lines +578 to +582
Option values with the following names are however discarded if included in the _operand_:

- `compactDisplay`
- `notation`
- `select`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just say that all options not named in the :percent function's list above are discarded?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be different from what we do with other functions, such as :integer, and it would make extensibility difficult.

An implementation could support additional options that are set in the calling code on the value being formatted, and are understood by the formatter. For a somewhat silly example, an implementation could support colour as a property of numbers, and format a "green 0.42" so that the % sign was also green in the output. If we here said that all options not explicitly listed are discarded, this kind of extensibility would not be possible.

Comment on lines +502 to +503
For the purposes of formatting,
the numeric value of the _operand_ is multiplied by 100.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ICU4C calls it "scale" or "multiplier"

ICU4X calls it "multiply_pow10"

I don't think we've had a proper bikeshedding session on this before. I've sort-of named it what I felt like naming it and no one ever pushed back with better names.

@eemeli
Copy link
Collaborator Author

eemeli commented Jan 27, 2025

During today's discussion, we noted that percent formatting is already available via :unit unit=percent.

This means that introducing :percent or retaining support for :number style=percent would be duplicating nearly the same functionality by two different means, and I don't think we should do that.

To still support the multiplicative behaviour (which :unit doesn't have), it might be best to add a :math multiply operator. @sffc raised some performance concerns about allowing multiplication by any number, and the digit-size option range (used by :math add and :math subtract) caps out at 99 for literals. Therefore, I'm considering refactoring this PR to:

  • Drop :number style and :integer style.
  • Add :math multiply with supported values 10, 100, 1000.

@aphillips
Copy link
Member

Add :math multiply with supported values 10, 100, 1000.

I'm dubious that this is a good idea. The :math function has a suggestive name. It's reasonable to assume that it will, over time, be extended to other purposes, similar to math functionality in programming languages (such as java.lang.Math in Java). It's convenient to solve specific short term problems using this function now, but we should avoid binding our hands too much.

Note that this would mean that unit=percent would become unscaled ({50 :unit unit=percent} formats as 50%). If we want to provide a :math modifier, perhaps scale or exp.

Also, there are other formats that we're ignoring (because they are much less common) which are supported by CLDR data, which is per-mille and per-myriad formatting. We should just solve the problem. The existence of these suggests a convenience function or maybe a convenience unit.

Consider:

.local $val = {|50|}
.local $pct = {$val :unit unit=percent-scaled}
{{I print {$pct} as 5000% but {$val :unit unit=percent} as 50%}}

@sffc
Copy link
Member

sffc commented Jan 28, 2025

I still have the weakly held position that :percent carries its own weight, and we should just add it and move on.

It doesn't completely duplicate functionality: you use :percent if you want scaling, and :unit unit=percent if not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEEDBACK] Please move style percent to its own function
3 participants