diff --git a/CHANGELOG.md b/CHANGELOG.md index 036f1671b25..160f1b107cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ - Added `total_cmp` functions to `Locale` and other types to make them easier to use in `BTreeSet` (https://github.com/unicode-org/icu4x/pull/4608) - `icu_locid_transform` - Add `LocaleExpander::minimize_favor_script` (https://github.com/unicode-org/icu4x/pull/4752) + - `icu_plurals` + - Added support for `CompactDecimal` (https://github.com/unicode-org/icu4x/pull/4828) - `icu_properties` - Add `Aran` script code (https://github.com/unicode-org/icu4x/pull/4426) - Mark additional constructors as `const` (https://github.com/unicode-org/icu4x/pull/4584, https://github.com/unicode-org/icu4x/pull/4574) @@ -48,6 +50,8 @@ - Deprecate `Hebrew::new_always_precomputing()`, `Date::try_new_hebrew_date_with_calendar()`, `DateTime::try_new_hebrew_datetime_with_calendar()`. The new implementation of the Hebrew calendar is faster and we do not need APIs for precomputation. (https://github.com/unicode-org/icu4x/pulls/4532) - `databake` - Add `impl Bake for PhantomData` (https://github.com/unicode-org/icu4x/pull/4663) + - `fixed_decimal` + - Changed type of compact exponent from `i16` to `u8` (https://github.com/unicode-org/icu4x/pull/4828) - `litemap` - Add `impl IntoIterator for LiteMap` by splitting `StoreIterableMut` trait (https://github.com/unicode-org/icu4x/pull/4359) - `yoke` diff --git a/components/experimental/src/compactdecimal/compactdecimal.rs b/components/experimental/src/compactdecimal/compactdecimal.rs index c9003914d5e..d98f9fe4ac2 100644 --- a/components/experimental/src/compactdecimal/compactdecimal.rs +++ b/components/experimental/src/compactdecimal/compactdecimal.rs @@ -629,14 +629,15 @@ impl CompactDecimalFormatter { &'l self, value: &'l CompactDecimal, ) -> Result, CompactDecimalError> { - let log10_type = value.significand().nonzero_magnitude_start() + value.exponent(); + let log10_type = + value.significand().nonzero_magnitude_start() + i16::from(value.exponent()); let (plural_map, expected_exponent) = self.plural_map_and_exponent_for_magnitude(log10_type); - if value.exponent() != i16::from(expected_exponent) { + if value.exponent() != expected_exponent { return Err(CompactDecimalError::Exponent { actual: value.exponent(), - expected: i16::from(expected_exponent), + expected: expected_exponent, log10_type, }); } diff --git a/components/experimental/src/compactdecimal/error.rs b/components/experimental/src/compactdecimal/error.rs index 29b7c529046..7a46acf3717 100644 --- a/components/experimental/src/compactdecimal/error.rs +++ b/components/experimental/src/compactdecimal/error.rs @@ -32,9 +32,9 @@ pub enum CompactDecimalError { #[displaydoc("Expected compact exponent {expected} for 10^{log10_type}, got {actual}")] Exponent { /// The compact decimal exponent passed to the formatter. - actual: i16, + actual: u8, /// The appropriate compact decimal exponent for a number of the given magnitude. - expected: i16, + expected: u8, /// The magnitude of the number being formatted. log10_type: i16, }, diff --git a/components/plurals/src/operands.rs b/components/plurals/src/operands.rs index ebb1803afeb..7a718f549b6 100644 --- a/components/plurals/src/operands.rs +++ b/components/plurals/src/operands.rs @@ -6,7 +6,7 @@ use core::isize; use core::num::ParseIntError; use core::str::FromStr; use displaydoc::Display; -use fixed_decimal::FixedDecimal; +use fixed_decimal::{CompactDecimal, FixedDecimal}; /// A full plural operands representation of a number. See [CLDR Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) for complete operands description. /// Plural operands in compliance with [CLDR Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules). @@ -235,25 +235,25 @@ macro_rules! impl_signed_integer_type { impl_integer_type!(u8 u16 u32 u64 u128 usize); impl_signed_integer_type!(i8 i16 i32 i64 i128 isize); -impl From<&FixedDecimal> for PluralOperands { - /// Converts a [`fixed_decimal::FixedDecimal`] to [`PluralOperands`]. Retains at most 18 - /// digits each from the integer and fraction parts. - fn from(dec: &FixedDecimal) -> Self { +impl PluralOperands { + fn from_significand_and_exponent(dec: &FixedDecimal, exp: u8) -> PluralOperands { + let exp_i16 = i16::from(exp); + let mag_range = dec.magnitude_range(); - let mag_high = core::cmp::min(17, *mag_range.end()); - let mag_low = core::cmp::max(-18, *mag_range.start()); + let mag_high = core::cmp::min(17, *mag_range.end() + exp_i16); + let mag_low = core::cmp::max(-18, *mag_range.start() + exp_i16); let mut i: u64 = 0; for magnitude in (0..=mag_high).rev() { i *= 10; - i += dec.digit_at(magnitude) as u64; + i += dec.digit_at(magnitude - exp_i16) as u64; } let mut f: u64 = 0; let mut t: u64 = 0; let mut w: usize = 0; for magnitude in (mag_low..=-1).rev() { - let digit = dec.digit_at(magnitude) as u64; + let digit = dec.digit_at(magnitude - exp_i16) as u64; f *= 10; f += digit; if digit != 0 { @@ -268,7 +268,66 @@ impl From<&FixedDecimal> for PluralOperands { w, f, t, - c: 0, + c: usize::from(exp), } } } + +impl From<&FixedDecimal> for PluralOperands { + /// Converts a [`fixed_decimal::FixedDecimal`] to [`PluralOperands`]. Retains at most 18 + /// digits each from the integer and fraction parts. + fn from(dec: &FixedDecimal) -> Self { + Self::from_significand_and_exponent(dec, 0) + } +} + +impl From<&CompactDecimal> for PluralOperands { + /// Converts a [`fixed_decimal::CompactDecimal`] to [`PluralOperands`]. Retains at most 18 + /// digits each from the integer and fraction parts. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::CompactDecimal; + /// use fixed_decimal::FixedDecimal; + /// use icu::locid::locale; + /// use icu::plurals::rules::RawPluralOperands; + /// use icu::plurals::PluralCategory; + /// use icu::plurals::PluralOperands; + /// use icu::plurals::PluralRules; + /// + /// let fixed_decimal = "1000000.20".parse::().unwrap(); + /// let compact_decimal = "1.00000020c6".parse::().unwrap(); + /// + /// assert_eq!( + /// PluralOperands::from(RawPluralOperands { + /// i: 1000000, + /// v: 2, + /// w: 1, + /// f: 20, + /// t: 2, + /// c: 0, + /// }), + /// PluralOperands::from(&fixed_decimal) + /// ); + /// + /// assert_eq!( + /// PluralOperands::from(RawPluralOperands { + /// i: 1000000, + /// v: 2, + /// w: 1, + /// f: 20, + /// t: 2, + /// c: 6, + /// }), + /// PluralOperands::from(&compact_decimal) + /// ); + /// + /// let rules = PluralRules::try_new_cardinal(&locale!("fr").into()).unwrap(); + /// assert_eq!(rules.category_for(&fixed_decimal), PluralCategory::Other); + /// assert_eq!(rules.category_for(&compact_decimal), PluralCategory::Many); + /// ``` + fn from(compact: &CompactDecimal) -> Self { + Self::from_significand_and_exponent(compact.significand(), compact.exponent()) + } +} diff --git a/tutorials/rust/baked/src/main.rs b/tutorials/rust/baked/src/main.rs index f1ac7c1a03c..b53dd4a5e87 100644 --- a/tutorials/rust/baked/src/main.rs +++ b/tutorials/rust/baked/src/main.rs @@ -18,7 +18,7 @@ impl_data_provider!(BakedProvider); fn main() { let rules = PluralRules::try_new_cardinal_unstable(&BakedProvider, &locale!("ru").into()) .expect("locale 'ru' should be present in the baked data"); - let result = rules.category_for(&3.into()); + let result = rules.category_for(3); assert_eq!(result, PluralCategory::Few); println!("{:?}", result); } diff --git a/tutorials/rust/custom_compiled/src/main.rs b/tutorials/rust/custom_compiled/src/main.rs index b58445870e3..31d8dfcb285 100644 --- a/tutorials/rust/custom_compiled/src/main.rs +++ b/tutorials/rust/custom_compiled/src/main.rs @@ -25,7 +25,7 @@ use icu::plurals::PluralRules; fn main() { let rules = PluralRules::try_new_cardinal(&locale!("ru").into()) .expect("locale 'ru' should be present in the compiled data"); - let result = rules.category_for(&3.into()); + let result = rules.category_for(3); assert_eq!(result, PluralCategory::Few); println!("{result:?}"); } diff --git a/tutorials/rust/default/src/main.rs b/tutorials/rust/default/src/main.rs index 2b19deee790..52670896dc1 100644 --- a/tutorials/rust/default/src/main.rs +++ b/tutorials/rust/default/src/main.rs @@ -13,7 +13,7 @@ use icu::plurals::PluralRules; fn main() { let rules = PluralRules::try_new_cardinal(&locale!("ru").into()) .expect("locale 'ru' should be present in the compiled data"); - let result = rules.category_for(&3.into()); + let result = rules.category_for(3); assert_eq!(result, PluralCategory::Few); println!("{result:?}"); } diff --git a/utils/fixed_decimal/src/compact.rs b/utils/fixed_decimal/src/compact.rs index f80d4796b6e..98af2e76dba 100644 --- a/utils/fixed_decimal/src/compact.rs +++ b/utils/fixed_decimal/src/compact.rs @@ -22,7 +22,7 @@ use crate::FixedDecimal; #[derive(Debug, Clone, PartialEq)] pub struct CompactDecimal { significand: FixedDecimal, - exponent: i16, + exponent: u8, } impl CompactDecimal { @@ -30,7 +30,7 @@ impl CompactDecimal { pub fn from_significand_and_exponent(significand: FixedDecimal, exponent: u8) -> Self { Self { significand, - exponent: exponent.into(), + exponent, } } @@ -74,7 +74,7 @@ impl CompactDecimal { /// assert_eq!(CompactDecimal::from_str("+1.20c6").unwrap().exponent(), 6); /// assert_eq!(CompactDecimal::from_str("1729").unwrap().exponent(), 0); /// ``` - pub fn exponent(&self) -> i16 { + pub fn exponent(&self) -> u8 { self.exponent } }