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

The actual formatting part of compact decimal formatting #2898

Merged
merged 103 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
0f2b754
maybe I should commit sometimes?
eggrobin Dec 7, 2022
1debe59
also the new files
eggrobin Dec 7, 2022
f1760a1
I forgot my laptop charger
eggrobin Dec 7, 2022
d5657bf
it appears to compile
eggrobin Dec 8, 2022
8b0f103
meow
eggrobin Dec 9, 2022
4d9f95f
plate the boiler
eggrobin Dec 9, 2022
43fe38e
a test that disturbingly passes
eggrobin Dec 9, 2022
aa6dc5c
I should probably have pushed that
eggrobin Dec 12, 2022
507e234
some refactoring and &&i8
eggrobin Dec 12, 2022
a5b63f6
logic is hard
eggrobin Dec 12, 2022
a0db0c9
long
eggrobin Dec 12, 2022
12bebe4
simpler end-to-end test
eggrobin Dec 13, 2022
7e60fe1
a test
eggrobin Dec 13, 2022
0c25367
more tests
eggrobin Dec 13, 2022
5f5db46
another test
eggrobin Dec 13, 2022
3dfd5bf
fix some warnings
eggrobin Dec 13, 2022
9537951
wrong warning is wrong
eggrobin Dec 13, 2022
b9f1405
use
eggrobin Dec 13, 2022
9ff37a2
the licensing will continue until morale improve
eggrobin Dec 13, 2022
22097a2
cargo dottore
eggrobin Dec 13, 2022
09cdad5
test all the errors
eggrobin Dec 13, 2022
addb602
readthee
eggrobin Dec 13, 2022
b9309e7
two
eggrobin Dec 13, 2022
21a35cf
appease clippy
eggrobin Dec 13, 2022
7373756
lints &c
eggrobin Dec 13, 2022
1c17a74
cargo make testdata
eggrobin Dec 13, 2022
e9c7bb1
logic remains hard
eggrobin Dec 14, 2022
89c14cb
cargo make testdata again
eggrobin Dec 14, 2022
a980ac0
feature propagation
eggrobin Dec 14, 2022
5265ca9
I am shocked, shocked, to find that escaping errors are going on in here
eggrobin Dec 14, 2022
0f3ce45
shuffle attributes between features
eggrobin Dec 14, 2022
040b22a
meow
eggrobin Dec 14, 2022
fb00543
no variant
eggrobin Dec 14, 2022
dd8616c
Allow noncompact CompactDecimal
eggrobin Dec 14, 2022
0b693bc
remove stray assertions
eggrobin Dec 14, 2022
8bbc3b2
noun
eggrobin Dec 14, 2022
15a7ccb
merge
eggrobin Dec 14, 2022
7127e5f
Update utils/fixed_decimal/src/compact.rs
eggrobin Dec 14, 2022
193e9b3
Update utils/fixed_decimal/src/compact.rs
eggrobin Dec 14, 2022
03afa3e
Update utils/fixed_decimal/src/compact.rs
eggrobin Dec 14, 2022
4115b83
meow
eggrobin Dec 14, 2022
e696837
Merge branch 'noncompact-compact' into the-actual-formatting-part
eggrobin Dec 14, 2022
8285b0a
dottore
eggrobin Dec 14, 2022
3760ec5
Merge branch 'noncompact-compact' into the-actual-formatting-part
eggrobin Dec 14, 2022
63e855b
some code that compiles
eggrobin Dec 14, 2022
ae21492
unstable is too stable
eggrobin Dec 15, 2022
b09a5e3
some more comments
eggrobin Dec 15, 2022
8d9f94d
refactor
eggrobin Dec 15, 2022
4a39f6b
use superstar
eggrobin Dec 15, 2022
5fbd7c1
zero
eggrobin Dec 15, 2022
4c92089
factor out something
eggrobin Dec 15, 2022
35d0180
comments
eggrobin Dec 15, 2022
2b55276
fmt
eggrobin Dec 15, 2022
64ca7a8
𒀀𒈾​𒎙𒎙​𒆠𒉈𒈠​𒌝𒈠​𒉭𒈠​𒆠𒀀𒄠​𒉈𒁉𒋳
eggrobin Dec 15, 2022
5b44901
cargo made testdata
eggrobin Dec 15, 2022
376b3c6
Merge branch 'compact-decimal-formatting' into the-actual-formatting-…
eggrobin Dec 15, 2022
80d051d
meow
eggrobin Dec 15, 2022
31d35b3
cargo make diplomat-coverage
eggrobin Dec 15, 2022
ad61326
mergez la vache
eggrobin Dec 15, 2022
c6e3d9d
cargo testdata gemacht
eggrobin Dec 15, 2022
3c6aa71
license
eggrobin Dec 15, 2022
3e6086b
fix test
eggrobin Dec 15, 2022
5b243bd
stray cfg
eggrobin Dec 15, 2022
4305c73
appease clippy
eggrobin Dec 16, 2022
ddb3bae
Merge branch 'noncompact-compact' into compact-decimal-formatting
eggrobin Dec 16, 2022
d2023b2
catch up
eggrobin Dec 16, 2022
60b9843
Merge remote-tracking branch 'la-vache/main' into compact-decimal-for…
eggrobin Dec 16, 2022
1a78ce4
Merge branch 'compact-decimal-formatting' into the-actual-formatting-…
eggrobin Dec 16, 2022
e94c2ce
Feels like YTI18N
eggrobin Dec 16, 2022
e16100a
fmt
eggrobin Dec 16, 2022
9d95dae
clippy
eggrobin Dec 16, 2022
1ce1827
Merge branch 'compact-decimal-formatting' into the-actual-formatting-…
eggrobin Dec 16, 2022
41d817f
maybe we needed that one after all
eggrobin Dec 16, 2022
e6ff2e8
Merge branch 'compact-decimal-formatting' into the-actual-formatting-…
eggrobin Dec 16, 2022
55f4b82
help the silly language along
eggrobin Dec 16, 2022
b2ae152
meow?
eggrobin Dec 16, 2022
b4336aa
Merge remote-tracking branch 'la-vache/main' into compact-decimal-for…
eggrobin Dec 16, 2022
0761f89
no into
eggrobin Dec 16, 2022
fa05f33
Merge branch 'compact-decimal-formatting' into the-actual-formatting-…
eggrobin Dec 16, 2022
061953d
reinstate into_iter1
eggrobin Dec 16, 2022
d64d299
Merge branch 'compact-decimal-formatting' into the-actual-formatting-…
eggrobin Dec 16, 2022
9538823
merge fallout
eggrobin Dec 16, 2022
a2df14d
somehow it works?
eggrobin Dec 16, 2022
42448d4
meow
eggrobin Dec 16, 2022
32b0e62
some documentation
eggrobin Dec 16, 2022
316f607
???
eggrobin Dec 16, 2022
b1b5887
𒁀𒉘𒆷
eggrobin Dec 16, 2022
aace046
merge
eggrobin Dec 16, 2022
fdbf234
English is weird
eggrobin Dec 16, 2022
b49caed
just do it
eggrobin Dec 19, 2022
9a9b7ae
fmt
eggrobin Dec 19, 2022
6ba4c27
appease clippy a little bit
eggrobin Dec 19, 2022
ca644e1
try to make the cargo doctor happy
eggrobin Dec 19, 2022
1479b06
&
eggrobin Dec 19, 2022
a9fafdd
'_ nonsense
eggrobin Dec 19, 2022
1258527
covered in diplomats
eggrobin Dec 19, 2022
dbe1258
invariants and correctness
eggrobin Dec 19, 2022
6b241f2
signs
eggrobin Dec 19, 2022
da5ec50
comments
eggrobin Dec 19, 2022
e64818c
Update experimental/compactdecimal/src/compactdecimal.rs
eggrobin Dec 20, 2022
c5184c9
oops, missed some comments
eggrobin Dec 20, 2022
ab62c18
renaming is hard
eggrobin Dec 20, 2022
287d8f7
more renaming
eggrobin Dec 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion experimental/compactdecimal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fixed_decimal = { version = "0.5", path = "../../utils/fixed_decimal" }
databake = { version = "0.1.0", path = "../../utils/databake", optional = true, features = ["derive"]}

[dev-dependencies]
icu_testdata = { path = "../../provider/testdata", default-features = false, features = ["icu_plurals", "icu_decimal"] }
icu_testdata = { path = "../../provider/testdata", default-features = false, features = ["icu_plurals", "icu_decimal", "icu_compactdecimal"] }
icu_locid = { version = "1.0.0", path = "../../components/locid" }

[features]
Expand Down
414 changes: 414 additions & 0 deletions experimental/compactdecimal/src/compactdecimal.rs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions experimental/compactdecimal/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ pub enum CompactDecimalError {
/// An error originating from [`FixedDecimalFormatter`](icu_decimal::FixedDecimalFormatter).
#[displaydoc("Error loading FixedDecimalFormatter: {0}")]
Decimal(DecimalError),
/// An error due to a [`CompactDecimal`](fixed_decimal::CompactDecimal) with an
/// exponent inconsistent with the compact decimal data for the locale, e.g.,
/// when formatting 1c5 in English (US).
#[displaydoc("Expected compact exponent {expected} for 10^{log10_type}, got {actual}")]
Exponent {
/// The compact decimal exponent passed to the formatter.
actual: i16,
/// The appropriate compact decimal exponent for a number of the given magnitude.
expected: i16,
/// The magnitude of the number being formatted.
log10_type: i16,
},
}

impl From<PluralsError> for CompactDecimalError {
Expand Down
74 changes: 74 additions & 0 deletions experimental/compactdecimal/src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use alloc::borrow::Cow;
use fixed_decimal::{CompactDecimal, FixedDecimal};
use writeable::Writeable;
use zerovec::maps::ZeroMap2dCursor;

use crate::compactdecimal::CompactDecimalFormatter;
use crate::provider::{Count, PatternULE};

/// An intermediate structure returned by [`CompactDecimalFormatter`](crate::CompactDecimalFormatter).
/// Use [`Writeable`][Writeable] to render the formatted decimal to a string or buffer.
pub struct FormattedCompactDecimal<'l> {
pub(crate) formatter: &'l CompactDecimalFormatter,
pub(crate) value: Cow<'l, CompactDecimal>,
pub(crate) plural_map: Option<ZeroMap2dCursor<'l, 'l, i8, Count, PatternULE>>,
Copy link
Member

Choose a reason for hiding this comment

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

thought: ought we pre-resolve the plural rule selection?

Copy link
Member

Choose a reason for hiding this comment

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

i guess it's tricky since it's not always needed

Copy link
Member

Choose a reason for hiding this comment

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

I had the same thought. There's not much use pre-resolving it since FormattedCompactDecimal is short-lived and generally intended to only be converted to a string or to parts; in fact, CompactDecimalFormatter::format is already doing more work than either FixedDecimalFormatter or DateTimeFormatter. Most of the work should live in FormattedCompactDecimal.

Copy link
Member Author

Choose a reason for hiding this comment

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

CompactDecimalFormatter::format is already doing more work than either FixedDecimalFormatter or DateTimeFormatter.

The reason for that is that the error checking of the fallible one (format_compact_decimal) needs to happen early enough that we can return a useful error (rather than core::fmt::Error).
That work being done, FormattedCompactDecimal naturally takes the shape it has.

}

impl<'l> Writeable for FormattedCompactDecimal<'l> {
fn write_to<W>(&self, sink: &mut W) -> core::result::Result<(), core::fmt::Error>
where
W: core::fmt::Write + ?Sized,
{
if self.value.exponent() == 0 {
self.formatter
.fixed_decimal_format
.format(self.value.significand())
.write_to(sink)
} else {
let plural_map = self.plural_map.as_ref().ok_or(core::fmt::Error)?;
let chosen_pattern = (|| {
if self.value.significand() == &FixedDecimal::from(1) {
if let Some(pattern) = plural_map.get1(&Count::Explicit1) {
return Some(pattern);
}
}
let plural_category = self
.formatter
.plural_rules
.category_for(self.value.significand());
plural_map
.get1(&plural_category.into())
.or_else(|| plural_map.get1(&Count::Other))
})()
.ok_or(core::fmt::Error)?;
Copy link
Member

Choose a reason for hiding this comment

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

note: IIFEs aren't super idiomatic in Rust, but the alternative is definitely uglier. being able to if foo && let ... will help in the future though.

Copy link
Member Author

Choose a reason for hiding this comment

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

TIL a new acronym.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, there's actually a nicer way to do this in Rust 1.65

https://stackoverflow.com/a/66629605/1407170

match chosen_pattern.index {
u8::MAX => sink.write_str(&chosen_pattern.literal_text),
_ => {
let i = usize::from(chosen_pattern.index);
sink.write_str(
chosen_pattern
.literal_text
.get(..i)
.ok_or(core::fmt::Error)?,
)?;
self.formatter
.fixed_decimal_format
.format(self.value.significand())
.write_to(sink)?;
Comment on lines +58 to +61
Copy link
Member

Choose a reason for hiding this comment

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

Issue: This might not work as expected with negative numbers. Please add some test cases for that (can be done after landing this PR).

The way FixedDecimalFormatter deals with signs (plus and minus) is to have 3 versions of the pattern that are selected at runtime: no sign, plus sign, and minus sign. So, we don't actually render a sign at runtime; we just select the pre-rendered pattern. I don't know if that's how we want to handle it in CDF since we have a lot more patterns and would like to avoid bloating the data.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added some examples in the docs tests; it works as I expect at least.

French mille is messy (though it doesn’t do something truly misleading like dropping the sign, it falls to that weird one case, thus -1 millier, which may be the least broken thing it can do; getting -mille would probably be tricky, and that looks utterly weird anyway).

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but try this in a locale where the sign and the compact affix are on the same side. For example:

https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-numbers-full/main/sw-KE/numbers.json

We have a pattern

              "1000000-count-other": "M0",

The correct output according to CLDR would be "+M3", but your code produces "M+3"

Copy link
Member

Choose a reason for hiding this comment

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

In that same file, there is

              "1000-count-one": "elfu 0;elfu -0",

which demonstrates how CLDR does actually want us to consider these as proper decimal patterns that could have different signed and unsigned versions.

Copy link
Member Author

@eggrobin eggrobin Dec 20, 2022

Choose a reason for hiding this comment

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

(argh, too many threads.)

Yes, but there is also elfu 0 without that dance, which is therefore suspect; and the millions don’t do that but do it in other variants of Swahili, etc.

See this in the datagen:

// All compact decimal patterns for sw (Swahili) are split by sign;
// the sign ends up where it would be as part of the significand, so
// this special handling is unneeded. Depending on the region subtag,
// the space may be breaking or nonbreaking.
("elfu 0;elfu -0", "elfu 0"),
("milioni 0;milioni -0", "milioni 0"),
("bilioni 0;bilioni -0", "bilioni 0"),
("trilioni 0;trilioni -0", "trilioni 0"),
("elfu\u{A0}0;elfu\u{A0}-0", "elfu\u{A0}0"),
("milioni\u{A0}0;milioni\u{A0}-0", "milioni\u{A0}0"),
("bilioni\u{A0}0;bilioni\u{A0}-0", "bilioni\u{A0}0"),
("trilioni\u{A0}0;trilioni\u{A0}-0", "trilioni\u{A0}0"),
("0M;-0M", "0M"),
("0B;-0B", "0B"),
("0T;-0T", "0B"),

I suspect we have bad data, but that having the sign next to the number is the better default, since many swahili patterns go out of their way to stick it back there.

Copy link
Member Author

Choose a reason for hiding this comment

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

"+M3" is likely a reasonable and correct outcome

I am unconvinced of that, given that it abbreviates something that seems to want to be milioni +3.

Bear in mind that while compact decimal is used quite a bit more than scientific, its edge cases (including the use of negative numbers) quickly get uncharted (after all, the Romance languages are only just starting not to be a flagrantly ungrammatical mess, and that is for positive values).

We will indeed need to deal with that for currencies (along with a number of other complexities).

Copy link
Member

Choose a reason for hiding this comment

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

We have some evidence from the number range work that designers have special requirements when it comes to single-character symbols. They tend to prefer single characters to be "glued" to the number, with extra annotations (like range separators and signs) displayed outside of the single-character symbol.

Copy link
Member

Choose a reason for hiding this comment

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

It's unambiguous that the current UTS 35 requires "+M3" instead of "M+3". Perhaps that data is wrong, but even if it is, there will continually be more cases where we need to handle cases like this. Should we appeal to CLDR to get clarity?

Copy link
Member Author

@eggrobin eggrobin Dec 20, 2022

Choose a reason for hiding this comment

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

Either way I need to open a CLDR ticket because it is clear that there is a lot of wrongness in the Swahili data, for instance
https://github.com/unicode-org/cldr-json/blob/f27f55f1dd487af7cf4260f56296ee1c7649b7fc/cldr-json/cldr-numbers-full/main/sw-KE/numbers.json#L34-L36
next to https://github.com/unicode-org/cldr-json/blob/f27f55f1dd487af7cf4260f56296ee1c7649b7fc/cldr-json/cldr-numbers-full/main/sw-KE/numbers.json#L62-L64.

If it turns out that something like sign prefix number etc. is needed for compact decimal (as opposed to compact currency) we can add a bit that says « sign goes at the beginning », but I suspect many of the patterns that do that (again, for compact decimal, not compact currency or ranges etc.) do that because CLDR hands the translators a handy footgun (which they work around as bugs come up).

Copy link
Member

Choose a reason for hiding this comment

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

If it turns out that something like sign prefix number etc. is needed for compact decimal (as opposed to compact currency) we can add a bit that says « sign goes at the beginning »

OK. Having only two valid places to put the sign (inside or outside the affix) is probably reasonable, but it's yet another invariant that we'll need to enforce in datagen. It does make things a bit easier and smaller.

sink.write_str(
chosen_pattern
.literal_text
.get(i..)
.ok_or(core::fmt::Error)?,
)
}
}
}
}
}

writeable::impl_display_with_writeable!(FormattedCompactDecimal<'_>);
5 changes: 4 additions & 1 deletion experimental/compactdecimal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
clippy::panic,
clippy::exhaustive_structs,
clippy::exhaustive_enums,
missing_debug_implementations,
// TODO(#2266): enable missing_debug_implementations,
)
)]
#![warn(missing_docs)]

extern crate alloc;

mod compactdecimal;
mod error;
mod format;
pub mod provider;

pub use compactdecimal::CompactDecimalFormatter;
pub use error::CompactDecimalError;
22 changes: 21 additions & 1 deletion experimental/compactdecimal/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
//! Read more about data providers: [`icu_provider`]

use alloc::borrow::Cow;
use icu_provider::{yoke, zerofrom};
use icu_plurals::PluralCategory;
use icu_provider::{yoke, zerofrom, DataMarker};
use zerovec::ZeroMap2d;

/// Relative time format V1 data struct.
Expand Down Expand Up @@ -89,6 +90,20 @@ pub enum Count {
// algorithm does not allow such a thing to arise.
}

impl From<PluralCategory> for Count {
fn from(other: PluralCategory) -> Self {
use PluralCategory::*;
match other {
Zero => Count::Zero,
One => Count::One,
Two => Count::Two,
Few => Count::Few,
Many => Count::Many,
Other => Count::Other,
}
}
}

/// A compact decimal pattern, representing some literal text with an optional
/// placeholder, and the power of 10 expressed by the text.
#[derive(
Expand Down Expand Up @@ -125,3 +140,8 @@ pub struct Pattern<'data> {
/// " M" for the pattern "000 M"
pub literal_text: Cow<'data, str>,
}
pub(crate) struct ErasedCompactDecimalFormatDataV1Marker;

impl DataMarker for ErasedCompactDecimalFormatDataV1Marker {
type Yokeable = CompactDecimalPatternDataV1<'static>;
}
2 changes: 2 additions & 0 deletions ffi/diplomat/ffi_coverage/tests/missing_apis.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
fixed_decimal::CompactDecimal#Struct
fixed_decimal::CompactDecimal::exponent#FnInStruct
fixed_decimal::CompactDecimal::from_significand_and_exponent#FnInStruct
fixed_decimal::CompactDecimal::from_str#FnInStruct
fixed_decimal::CompactDecimal::into_significand#FnInStruct
fixed_decimal::CompactDecimal::significand#FnInStruct
fixed_decimal::CompactDecimal::write_to#FnInStruct
fixed_decimal::FixedInteger#Struct
fixed_decimal::FixedInteger::from_str#FnInStruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,22 @@ impl TryFrom<&DecimalFormat> for CompactDecimalPatternDataV1<'static> {
);
}
}
if !patterns
.iter()
.tuple_windows()
.all(|((_, low), (_, high))| {
low.get(&Count::Other).map(|p| p.exponent)
<= high.get(&Count::Other).map(|p| p.exponent)
})
{
Err(format!(
"Compact decimal exponents should be nondecreasing: {:?}",
patterns
.iter()
.map(|(_, plural_map)| plural_map.get(&Count::Other).map(|p| p.exponent))
.collect::<Vec<_>>(),
))?;
}
// Deduplicate sequences of types that have the same plural map (up to =1), keeping the lowest type.
// The pattern 0 for type 1 is implicit.
let deduplicated_patterns = patterns
Expand Down
1 change: 1 addition & 0 deletions provider/testdata/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ zerovec = { version = "0.9", path = "../../utils/zerovec" }
icu_calendar = { version = "1.0.0", path = "../../components/calendar", default-features = false, optional = true }
icu_casemapping = { version = "0.7.0", path = "../../experimental/casemapping", default-features = false, optional = true }
icu_collator = { version = "1.0.0", path = "../../components/collator", default-features = false, optional = true }
icu_compactdecimal = { version = "0.1.0", path = "../../experimental/compactdecimal", default-features = false, optional = true }
icu_datetime = { version = "1.0.0", path = "../../components/datetime", default-features = false, optional = true }
icu_decimal = { version = "1.0.0", path = "../../components/decimal", default-features = false, optional = true }
icu_displaynames = { version = "0.7.0", path = "../../experimental/displaynames", default-features = false, optional = true }
Expand Down
20 changes: 20 additions & 0 deletions utils/fixed_decimal/src/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ pub struct CompactDecimal {
}

impl CompactDecimal {
/// Constructs a [`CompactDecimal`] from its significand and exponent.
pub fn from_significand_and_exponent(significand: FixedDecimal, exponent: u8) -> Self {
Self {
significand,
exponent: exponent.into(),
}
}

/// Returns a reference to the significand of `self`.
/// ```
/// # use fixed_decimal::CompactDecimal;
/// # use fixed_decimal::FixedDecimal;
/// # use std::str::FromStr;
/// #
/// assert_eq!(CompactDecimal::from_str("+1.20c6").unwrap().significand(), &FixedDecimal::from_str("+1.20").unwrap());
/// ```
pub fn significand(&self) -> &FixedDecimal {
eggrobin marked this conversation as resolved.
Show resolved Hide resolved
&self.significand
}

/// Returns the significand of `self`.
/// ```
/// # use fixed_decimal::CompactDecimal;
Expand Down