3.0.0 update
Completely remade the module.
You can safely ignore many of my old statement, these no longer apply in version 3, things have been changed.
I based it around the ICU’s NumberFormatter API and simplified the module, removing features that complicates the implementation (formatting to parts, number range formatting) with not many use cases outside of i18n.
Why NumberFormatter class?
There isn’t any reason behind it, just a design decision and doesn’t really make the module more complicated. I’d say it’d make the implementation easier as it doesn’t have to resolve options and check types every time the function calls.
This way, you can have multiple formatters with different settings. It’s easier to change settings this way.
Old post
I’ve remade this module. With more user-friendly functions, and it’s easy to use, just call the function with the value as the argument, with an optional option
argument if you’re not satsified with the default!
2.3.0
FormatNumber.rbxm (12.3 KB)
2.2.0
FormatNumber.rbxm
2.1.0
FormatNumber.rbxm (7.8 KB)
2.0.1
FormatNumber.rbxm (7.3 KB)
2.0.0
FormatNumber.rbxm (7.3 KB)
Features
- Negaive number, decimal, infinity and NaN support.
- BigNum/BigInt and numeric string support
- Range formatting
- Formatting to parts.
Does not include
- Unit formatting.
- Pluralised number abbreviation.
- Currency names
Functions
string FormatNumber.FormatStandard(number/BigNum/BigInt value, table options)
Format numbers in the standard pattern.
Example
print(FormatNumber.FormatStandard(1234.56)) --> 1,234.56
print(FormatNumber.FormatStandard(-1234.56)) --> -1,234.56
print(FormatNumber.FormatStandard(0/0)) --> NaN
string FormatNumber.FormatCompact(number/BigNum/BigInt value, table options)
Want to abbreviate numbers? No problem. Uses @berezaa’s suffixes (MoneyLib) and up to 10^308
.
Example
print(FormatNumber.FormatCompact(1234)) --> 1.2k
print(FormatNumber.FormatCompact(12345)) --> 12k
string FormatNumber.FormatScientific(number/BigNum/BigInt start_value, number/BigNum/BigInt, end_value, table options)
Show numbers in scientific notation. Supports engineering
string, string FormatNumber.FormatStandardRange(number/BigNum/BigInt start_value, number/BigNum/BigInt end_value, table options)
string FormatNumber.FormatCompactRange(number/BigNum/BigInt start_value, number/BigNum/BigInt end_value, table options)
string, string FormatNumber.FormatScientificRange(number/BigNum/BigInt start_value, number/BigNum/BigInt end_value, table options)
Format numbers in range, experimental. (Expect for FormatCompactRange which only returns 1 value (don’t ask), it returns 2 value, the 1st is the formatted value while the 2nd is the rounding result)
print((FormatNumber.FormatStandardRange(1234, 12345))) --> 1,234–12,345
print((FormatNumber.FormatStandardRange(-math.huge, math.huge))) --> -∞–∞
print((FormatNumber.FormatCompactRange(1000, 2000))) --> 1k–2k
table FormatNumber.FormatStandardToParts(number/BigNum/BigInt value, table options)
table FormatNumber.FormatCompactToParts(number/BigNum/BigInt value, table options)
table FormatNumber.FormatScientificToParts(number/BigNum/BigInt value, table options)
Format numbers to parts, experimental.
type | meaning |
---|---|
integer | The integral part of the value |
decimal | The decimal symbol |
group | The grouping symbol |
fraction | The fraction part of the value |
minusSign | The minus sign |
plusSign | The plus sign |
currency | The currency value |
literal | The literal value |
nan | The Not-a-Number value |
infinity | The infinity value |
table Format
Number.AbbreviationToCLDR(table abbreviations, boolean include_currency)
Converts abbreviation suffixes of 3 digits to Unicode CLDR compact number pattern so you can use it on compactPattern
as that only accept that “CLDR”-styled pattern (no plurals though).
Example FormatNumber.AbbreviationsToCLDR{'k', 'M', 'B', 'T'}
converts it to {'0k', '00k', '000k', '0M', '00M', '000M', '0B', '00B', '000B', '0T', '00T', '000T'}
Options
Applies to FormatStandard and FormatCompact
groupSymbol
The grouping symbol, the default is ,
decimalSymbol
The decimal symbol, the default is .
useGrouping
Determine the number should be grouped. This modules assumes the default minimum grouping digits is 1 so:
-
"always"
group the number (default forFormatStandard
) (Trivial info: It also ignores locales that disables grouping like bg currency and the en-US-POSIX locale and still group the number) -
"min2"
group the number only if it has 5 digits or over (default forFormatCompact
) (Trivial info: The actual function is locale dependant and it actually means the minimum grouping digits will be overrided to 2 if it’s lower than 2, so if the default minimum grouping digits is 3 like locale ee or 4 like locale hu before CLDR 36, it still won’t group values below 6 digits, this info does not matter here and this can be safely ignored) -
"never"
, don’t group the number
Trivial info: The "auto"
value isn’t supported because that’s locale dependant, and is redundant because the default minimum grouping digits is assumed to be 1 and “always” sets the minimum grouping digits to 1
style
The formatting style to use
-
"decimal"
plain number formatting -
"currency"
currency number formatting -
"percent"
percent formatting
Trivial info: The "unit"
value isn’t supported because it doesn’t support unit formatting
currency
The currency to format, (For this module, this is a currency symbol, while in International, this is the ISO 4217 currency code)
rounding
Rounding types
-
"halfEven"
rounds to the nearest even in when it’s halfway or over (default) -
"halfUp"
rounds up the value when it’s halfway or over -
"halfDown"
rounds down the value when it’s halfway or over -
"down"
rounds down the value -
"up"
rounds up the value
only available on FormatCompact
and FormatCompactRange
compactPattern
The compact pattern in tables, it starts at thousands so if the value is 1000 and the pattern is {'0K'}
, it’ll format it as 1K
The pattern string is based around Unicode CLDR’s, ;
for separation of positive and negative, ¤
for currency placeholder, etc. Keep in mind 0
fallbacks to FormatStandard
and any value below 1000 have a pattern of 0
and only the 0
(size) and literal pattern (’) are suppored.
For example if I insert the value of 12345
pattern | formatted |
---|---|
0 | 12,345 |
0 ten thousand | 1.2 ten thousand |
00k | 12k |
The default is the abbreviation pattern is ber’s Miner Haven, it’ll format values similarly to MoneyLib (except that values over 10^309 won’t be in scientific notation)
If at least one of the minimumSignificantDigits
and maximumSignificantDigits
option is not nil
, minimumFractionDigits
, maximumFractionDigits
, minimumIntegerDigits
and maximumIntegerDigits
are ignored, and (Only applies to FormatCompact
and FormatCompactRange
) if at least one of the minimumSignificantDigits
, maximumSignificantDigits
, minimumFractionDigits
, maximumFractionDigits
, minimumIntegerDigits
and maximumIntegerDigits
option is not nil
, minimumSignificantDigitsToKeep
and trailingZeroesIfRounded
are ignored.
minimumFractionDigits
The minimum of fractional digit to use, defaults to 0 (unlike International.NumberFormat which defaults depending on the ISO 4217 currency entered if the style is currency, this still defaults to 0 if the style is currency)
maximumFractionDigits
The maximum of fracitonal digit to use, defaults to 3, use math.huge
for unlimited maximum fraciton digits.
minimumIntegerDigits
The minimum of integral digits to use (Zero padded), for example if the minimum integer digit is 2 and 1
is entered, it’ll format it as 01
, defaults to 1
maximumIntegerDigits
(undocumented)
minimumSignificantDigits
The minimum of significant digits to use.
maximumSignificantDigits
The maximum of significant digits to use.
experemental
only available on FormatCompact
and FormatCompactRange
minimumSignificantDigitsToKeep
The minimum significant digits to keep, and don’t round those, defaults to 2.
only available on FormatCompact
and FormatCompactRange
trailingZeroesIfRounded
The minimum trailing zeroes if the FormatCompact/FormatCompactRange is rounded, defaults to 0.
Example
value | trailingZeroesIfRounded | minimumSignificantDigitsToKeep | rounded/trauncated |
---|---|---|---|
1 | 0 | 2 | 1 |
1.2 | 0 | 2 | 1.2 |
9.87 | 0 | 2 | 9.8 |
10 | 0 | 2 | 10 |
12.3 | 0 | 2 | 12 |
98.76 | 0 | 2 | 98.7 |
100 | 0 | 2 | 100 |
123.4 | 0 | 2 | 123 |
987.65 | 0 | 2 | 987 |
1000 | 0 | 2 | 1000 |
1234.5 | 0 | 2 | 1234 |
9876.54 | 0 | 2 | 9876 |
10,000 | 0 | 2 | 10,000 |
98,765.43 | 0 | 2 | 98,765 |
12,345.6 | 0 | 2 | 12,345 |
1 | 0 | 3 | 1 |
1.2 | 0 | 3 | 1.2 |
9.87 | 0 | 3 | 9.87 |
10 | 0 | 3 | 10 |
12.3 | 0 | 3 | 12.3 |
98.76 | 0 | 3 | 98.7 |
100 | 0 | 3 | 100 |
123.4 | 0 | 3 | 123 |
987.65 | 0 | 3 | 987 |
1000 | 0 | 3 | 1000 |
1234.5 | 0 | 3 | 1234 |
9876.54 | 0 | 3 | 9876 |
10,000 | 0 | 3 | 10,000 |
98,765.43 | 0 | 3 | 98,765 |
12,345.6 | 0 | 3 | 12,345 |
1 | 1 | 2 | 1.0 |
1.2 | 1 | 2 | 1.2 |
9.87 | 1 | 2 | 9.8 |
10 | 1 | 2 | 10 |
12.3 | 1 | 2 | 12 |
98.76 | 1 | 2 | 98.7 |
1 | 2 | 3 | 1.00 |
1.2 | 2 | 3 | 1.20 |
9.87 | 2 | 3 | 9.87 |
10 | 2 | 3 | 10.0 |
12.3 | 2 | 3 | 12.3 |
98.76 | 2 | 3 | 98.7 |
100 | 2 | 3 | 100 |
123.4 | 2 | 3 | 123 |
987.65 | 2 | 3 | 987 |
1 | 1 | 3 | 1.0 |
1.2 | 1 | 3 | 1.2 |
9.87 | 1 | 3 | 9.87 |
10 | 1 | 3 | 10 |
12.3 | 1 | 3 | 12.3 |
98.76 | 1 | 3 | 98.7 |
Only available on FormatScientific and FormatScientificRange
engineering
Show in exponent in 10s only when it’s divisble by three, defaults to false
exponentLowercased
experemental, might change
Shows the exponent symbol as e
instead of E
, defaults to false
Question
Is it really easy to use?
Yep, all you need to is just call the function, with the value argument and if you’re not happy you can create a dictionary for the option argument with options you can change.
print(FormatNumber.FormatStandard(1000)) --> 1,000
print(FormatNumber.FormatCompact(1234)) --> 1.2k
Why was min2 the default value for useGrouping for numbers abbreviations?
International originally have two options for grouping: true and false, true
maps to "auto"
and false
maps to "never"
for standard notation however true
maps to "min2"
for compact notation/number abbreviation, the reason for this is was back then I was trying to replicate ECMA 402 behaviour. On the 2.1 update, I added more grouping options, and min2 became the default for grouping for compact notation and that’s the hang over from it.
You might also notice ECMA 402 also default min2 as grouping for compact notation (and still uses booleans for useGrouping option):
ECMA 402 does this because Unicode ICU defaulting grouping for compact notation to be min2.
That’s the way it is and it had always been that way. In summary, the behaviour is a hangover from International which is a hangover from ECMA 402 which uses Unicode ICU default grouping for compact notation.
Is this locale-aware?
No. If you want a locale aware version, see International. This is a subset of International so it might feel similar.