From 09edcd71e5e4ad68c5dffb1ebbd341144340dbd0 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 1 Nov 2023 18:10:50 -0600 Subject: [PATCH 1/7] Implement remaining round to increment methods --- ffi/capi/c/include/ICU4XFixedDecimal.h | 14 + .../cpp/docs/source/fixed_decimal_ffi.rst | 35 + ffi/capi/cpp/include/ICU4XFixedDecimal.h | 14 + ffi/capi/cpp/include/ICU4XFixedDecimal.hpp | 56 + .../package/docs/source/fixed_decimal_ffi.rst | 35 + .../js/package/lib/ICU4XFixedDecimal.d.ts | 42 + ffi/capi/js/package/lib/ICU4XFixedDecimal.js | 28 + ffi/capi/src/fixed_decimal.rs | 78 ++ utils/fixed_decimal/src/decimal.rs | 1244 ++++++++++++++++- 9 files changed, 1511 insertions(+), 35 deletions(-) diff --git a/ffi/capi/c/include/ICU4XFixedDecimal.h b/ffi/capi/c/include/ICU4XFixedDecimal.h index 361294b6874..42f91a75adf 100644 --- a/ffi/capi/c/include/ICU4XFixedDecimal.h +++ b/ffi/capi/c/include/ICU4XFixedDecimal.h @@ -75,20 +75,34 @@ void ICU4XFixedDecimal_trunc(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_trunc(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_trunc_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_expand(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_expand(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_expand_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_ceil(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_half_ceil(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_floor(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_half_floor(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_half_even(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_even_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + diplomat_result_void_void ICU4XFixedDecimal_concatenate_end(ICU4XFixedDecimal* self, ICU4XFixedDecimal* other); void ICU4XFixedDecimal_to_string(const ICU4XFixedDecimal* self, DiplomatWriteable* to); diff --git a/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst b/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst index 7adcddfc75d..eede7eb7106 100644 --- a/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst +++ b/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst @@ -172,6 +172,11 @@ See the `Rust documentation for half_trunc `__ for more information. + .. cpp:function:: void half_trunc_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for half_trunc_to_increment `__ for more information. + + .. cpp:function:: void expand(int16_t position) See the `Rust documentation for expand `__ for more information. @@ -182,31 +187,61 @@ See the `Rust documentation for half_expand `__ for more information. + .. cpp:function:: void half_expand_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for half_expand_to_increment `__ for more information. + + .. cpp:function:: void ceil(int16_t position) See the `Rust documentation for ceil `__ for more information. + .. cpp:function:: void ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for ceil_to_increment `__ for more information. + + .. cpp:function:: void half_ceil(int16_t position) See the `Rust documentation for half_ceil `__ for more information. + .. cpp:function:: void half_ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for half_ceil_to_increment `__ for more information. + + .. cpp:function:: void floor(int16_t position) See the `Rust documentation for floor `__ for more information. + .. cpp:function:: void floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for floor_to_increment `__ for more information. + + .. cpp:function:: void half_floor(int16_t position) See the `Rust documentation for half_floor `__ for more information. + .. cpp:function:: void half_floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for half_floor_to_increment `__ for more information. + + .. cpp:function:: void half_even(int16_t position) See the `Rust documentation for half_even `__ for more information. + .. cpp:function:: void half_even_to_increment(int16_t position, ICU4XRoundingIncrement increment) + + See the `Rust documentation for half_even_to_increment `__ for more information. + + .. cpp:function:: diplomat::result concatenate_end(ICU4XFixedDecimal& other) Concatenates ``other`` to the end of ``self``. diff --git a/ffi/capi/cpp/include/ICU4XFixedDecimal.h b/ffi/capi/cpp/include/ICU4XFixedDecimal.h index 361294b6874..42f91a75adf 100644 --- a/ffi/capi/cpp/include/ICU4XFixedDecimal.h +++ b/ffi/capi/cpp/include/ICU4XFixedDecimal.h @@ -75,20 +75,34 @@ void ICU4XFixedDecimal_trunc(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_trunc(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_trunc_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_expand(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_expand(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_expand_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_ceil(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_half_ceil(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_floor(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_half_floor(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + void ICU4XFixedDecimal_half_even(ICU4XFixedDecimal* self, int16_t position); +void ICU4XFixedDecimal_half_even_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); + diplomat_result_void_void ICU4XFixedDecimal_concatenate_end(ICU4XFixedDecimal* self, ICU4XFixedDecimal* other); void ICU4XFixedDecimal_to_string(const ICU4XFixedDecimal* self, DiplomatWriteable* to); diff --git a/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp b/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp index fed2ac629a8..740f00cf8c4 100644 --- a/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp +++ b/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp @@ -199,6 +199,11 @@ class ICU4XFixedDecimal { */ void half_trunc(int16_t position); + /** + * See the [Rust documentation for `half_trunc_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_trunc_to_increment) for more information. + */ + void half_trunc_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * See the [Rust documentation for `expand`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.expand) for more information. */ @@ -209,31 +214,61 @@ class ICU4XFixedDecimal { */ void half_expand(int16_t position); + /** + * See the [Rust documentation for `half_expand_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_expand_to_increment) for more information. + */ + void half_expand_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * See the [Rust documentation for `ceil`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil) for more information. */ void ceil(int16_t position); + /** + * See the [Rust documentation for `ceil_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil_to_increment) for more information. + */ + void ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * See the [Rust documentation for `half_ceil`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil) for more information. */ void half_ceil(int16_t position); + /** + * See the [Rust documentation for `half_ceil_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil_to_increment) for more information. + */ + void half_ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * See the [Rust documentation for `floor`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor) for more information. */ void floor(int16_t position); + /** + * See the [Rust documentation for `floor_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor_to_increment) for more information. + */ + void floor_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * See the [Rust documentation for `half_floor`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor) for more information. */ void half_floor(int16_t position); + /** + * See the [Rust documentation for `half_floor_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor_to_increment) for more information. + */ + void half_floor_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * See the [Rust documentation for `half_even`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even) for more information. */ void half_even(int16_t position); + /** + * See the [Rust documentation for `half_even_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even_to_increment) for more information. + */ + void half_even_to_increment(int16_t position, ICU4XRoundingIncrement increment); + /** * Concatenates `other` to the end of `self`. * @@ -382,27 +417,48 @@ inline void ICU4XFixedDecimal::trunc(int16_t position) { inline void ICU4XFixedDecimal::half_trunc(int16_t position) { capi::ICU4XFixedDecimal_half_trunc(this->inner.get(), position); } +inline void ICU4XFixedDecimal::half_trunc_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_half_trunc_to_increment(this->inner.get(), position, static_cast(increment)); +} inline void ICU4XFixedDecimal::expand(int16_t position) { capi::ICU4XFixedDecimal_expand(this->inner.get(), position); } inline void ICU4XFixedDecimal::half_expand(int16_t position) { capi::ICU4XFixedDecimal_half_expand(this->inner.get(), position); } +inline void ICU4XFixedDecimal::half_expand_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_half_expand_to_increment(this->inner.get(), position, static_cast(increment)); +} inline void ICU4XFixedDecimal::ceil(int16_t position) { capi::ICU4XFixedDecimal_ceil(this->inner.get(), position); } +inline void ICU4XFixedDecimal::ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_ceil_to_increment(this->inner.get(), position, static_cast(increment)); +} inline void ICU4XFixedDecimal::half_ceil(int16_t position) { capi::ICU4XFixedDecimal_half_ceil(this->inner.get(), position); } +inline void ICU4XFixedDecimal::half_ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_half_ceil_to_increment(this->inner.get(), position, static_cast(increment)); +} inline void ICU4XFixedDecimal::floor(int16_t position) { capi::ICU4XFixedDecimal_floor(this->inner.get(), position); } +inline void ICU4XFixedDecimal::floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_floor_to_increment(this->inner.get(), position, static_cast(increment)); +} inline void ICU4XFixedDecimal::half_floor(int16_t position) { capi::ICU4XFixedDecimal_half_floor(this->inner.get(), position); } +inline void ICU4XFixedDecimal::half_floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_half_floor_to_increment(this->inner.get(), position, static_cast(increment)); +} inline void ICU4XFixedDecimal::half_even(int16_t position) { capi::ICU4XFixedDecimal_half_even(this->inner.get(), position); } +inline void ICU4XFixedDecimal::half_even_to_increment(int16_t position, ICU4XRoundingIncrement increment) { + capi::ICU4XFixedDecimal_half_even_to_increment(this->inner.get(), position, static_cast(increment)); +} inline diplomat::result ICU4XFixedDecimal::concatenate_end(ICU4XFixedDecimal& other) { auto diplomat_result_raw_out_value = capi::ICU4XFixedDecimal_concatenate_end(this->inner.get(), other.AsFFIMut()); diplomat::result diplomat_result_out_value; diff --git a/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst b/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst index 354705dc5cc..ecc622cdf0b 100644 --- a/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst +++ b/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst @@ -172,6 +172,11 @@ See the `Rust documentation for half_trunc `__ for more information. + .. js:method:: half_trunc_to_increment(position, increment) + + See the `Rust documentation for half_trunc_to_increment `__ for more information. + + .. js:method:: expand(position) See the `Rust documentation for expand `__ for more information. @@ -182,31 +187,61 @@ See the `Rust documentation for half_expand `__ for more information. + .. js:method:: half_expand_to_increment(position, increment) + + See the `Rust documentation for half_expand_to_increment `__ for more information. + + .. js:method:: ceil(position) See the `Rust documentation for ceil `__ for more information. + .. js:method:: ceil_to_increment(position, increment) + + See the `Rust documentation for ceil_to_increment `__ for more information. + + .. js:method:: half_ceil(position) See the `Rust documentation for half_ceil `__ for more information. + .. js:method:: half_ceil_to_increment(position, increment) + + See the `Rust documentation for half_ceil_to_increment `__ for more information. + + .. js:method:: floor(position) See the `Rust documentation for floor `__ for more information. + .. js:method:: floor_to_increment(position, increment) + + See the `Rust documentation for floor_to_increment `__ for more information. + + .. js:method:: half_floor(position) See the `Rust documentation for half_floor `__ for more information. + .. js:method:: half_floor_to_increment(position, increment) + + See the `Rust documentation for half_floor_to_increment `__ for more information. + + .. js:method:: half_even(position) See the `Rust documentation for half_even `__ for more information. + .. js:method:: half_even_to_increment(position, increment) + + See the `Rust documentation for half_even_to_increment `__ for more information. + + .. js:method:: concatenate_end(other) Concatenates ``other`` to the end of ``self``. diff --git a/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts b/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts index dfa978cbfd4..93aa704fbd0 100644 --- a/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts +++ b/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts @@ -207,6 +207,12 @@ export class ICU4XFixedDecimal { */ half_trunc(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_trunc_to_increment Rust documentation for `half_trunc_to_increment`} for more information. + */ + half_trunc_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.expand Rust documentation for `expand`} for more information. @@ -219,36 +225,72 @@ export class ICU4XFixedDecimal { */ half_expand(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_expand_to_increment Rust documentation for `half_expand_to_increment`} for more information. + */ + half_expand_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil Rust documentation for `ceil`} for more information. */ ceil(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil_to_increment Rust documentation for `ceil_to_increment`} for more information. + */ + ceil_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil Rust documentation for `half_ceil`} for more information. */ half_ceil(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil_to_increment Rust documentation for `half_ceil_to_increment`} for more information. + */ + half_ceil_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor Rust documentation for `floor`} for more information. */ floor(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor_to_increment Rust documentation for `floor_to_increment`} for more information. + */ + floor_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor Rust documentation for `half_floor`} for more information. */ half_floor(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor_to_increment Rust documentation for `half_floor_to_increment`} for more information. + */ + half_floor_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even Rust documentation for `half_even`} for more information. */ half_even(position: i16): void; + /** + + * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even_to_increment Rust documentation for `half_even_to_increment`} for more information. + */ + half_even_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; + /** * Concatenates `other` to the end of `self`. diff --git a/ffi/capi/js/package/lib/ICU4XFixedDecimal.js b/ffi/capi/js/package/lib/ICU4XFixedDecimal.js index e517bc9f48c..8db2542a720 100644 --- a/ffi/capi/js/package/lib/ICU4XFixedDecimal.js +++ b/ffi/capi/js/package/lib/ICU4XFixedDecimal.js @@ -190,6 +190,10 @@ export class ICU4XFixedDecimal { wasm.ICU4XFixedDecimal_half_trunc(this.underlying, arg_position); } + half_trunc_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_half_trunc_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + expand(arg_position) { wasm.ICU4XFixedDecimal_expand(this.underlying, arg_position); } @@ -198,26 +202,50 @@ export class ICU4XFixedDecimal { wasm.ICU4XFixedDecimal_half_expand(this.underlying, arg_position); } + half_expand_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_half_expand_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + ceil(arg_position) { wasm.ICU4XFixedDecimal_ceil(this.underlying, arg_position); } + ceil_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_ceil_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + half_ceil(arg_position) { wasm.ICU4XFixedDecimal_half_ceil(this.underlying, arg_position); } + half_ceil_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_half_ceil_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + floor(arg_position) { wasm.ICU4XFixedDecimal_floor(this.underlying, arg_position); } + floor_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_floor_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + half_floor(arg_position) { wasm.ICU4XFixedDecimal_half_floor(this.underlying, arg_position); } + half_floor_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_half_floor_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + half_even(arg_position) { wasm.ICU4XFixedDecimal_half_even(this.underlying, arg_position); } + half_even_to_increment(arg_position, arg_increment) { + wasm.ICU4XFixedDecimal_half_even_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); + } + concatenate_end(arg_other) { return (() => { const is_ok = wasm.ICU4XFixedDecimal_concatenate_end(this.underlying, arg_other.underlying) == 1; diff --git a/ffi/capi/src/fixed_decimal.rs b/ffi/capi/src/fixed_decimal.rs index 64396983236..839705f8ea2 100644 --- a/ffi/capi/src/fixed_decimal.rs +++ b/ffi/capi/src/fixed_decimal.rs @@ -244,6 +244,20 @@ pub mod ffi { self.0.half_trunc(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_trunc_to_increment, FnInStruct)] + #[diplomat::rust_link( + fixed_decimal::FixedDecimal::half_trunced_to_increment, + FnInStruct, + hidden + )] + pub fn half_trunc_to_increment( + &mut self, + position: i16, + increment: ICU4XRoundingIncrement, + ) { + self.0.half_trunc_to_increment(position, increment.into()) + } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::expand, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::expanded, FnInStruct, hidden)] pub fn expand(&mut self, position: i16) { @@ -256,36 +270,100 @@ pub mod ffi { self.0.half_expand(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_expand_to_increment, FnInStruct)] + #[diplomat::rust_link( + fixed_decimal::FixedDecimal::half_expanded_to_increment, + FnInStruct, + hidden + )] + pub fn half_expand_to_increment( + &mut self, + position: i16, + increment: ICU4XRoundingIncrement, + ) { + self.0.half_expand_to_increment(position, increment.into()) + } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceil, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceiled, FnInStruct, hidden)] pub fn ceil(&mut self, position: i16) { self.0.ceil(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceil_to_increment, FnInStruct)] + #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceiled_to_increment, FnInStruct, hidden)] + pub fn ceil_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { + self.0.ceil_to_increment(position, increment.into()) + } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceil, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceiled, FnInStruct, hidden)] pub fn half_ceil(&mut self, position: i16) { self.0.half_ceil(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceil_to_increment, FnInStruct)] + #[diplomat::rust_link( + fixed_decimal::FixedDecimal::half_ceiled_to_increment, + FnInStruct, + hidden + )] + pub fn half_ceil_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { + self.0.half_ceil_to_increment(position, increment.into()) + } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::floor, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::floored, FnInStruct, hidden)] pub fn floor(&mut self, position: i16) { self.0.floor(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::floor_to_increment, FnInStruct)] + #[diplomat::rust_link( + fixed_decimal::FixedDecimal::floored_to_increment, + FnInStruct, + hidden + )] + pub fn floor_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { + self.0.floor_to_increment(position, increment.into()) + } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floor, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floored, FnInStruct, hidden)] pub fn half_floor(&mut self, position: i16) { self.0.half_floor(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floor_to_increment, FnInStruct)] + #[diplomat::rust_link( + fixed_decimal::FixedDecimal::half_floored_to_increment, + FnInStruct, + hidden + )] + pub fn half_floor_to_increment( + &mut self, + position: i16, + increment: ICU4XRoundingIncrement, + ) { + self.0.half_floor_to_increment(position, increment.into()) + } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_even, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_evened, FnInStruct, hidden)] pub fn half_even(&mut self, position: i16) { self.0.half_even(position) } + #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_even_to_increment, FnInStruct)] + #[diplomat::rust_link( + fixed_decimal::FixedDecimal::half_evened_to_increment, + FnInStruct, + hidden + )] + pub fn half_even_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { + self.0.half_even_to_increment(position, increment.into()) + } + /// Concatenates `other` to the end of `self`. /// /// If successful, `other` will be set to 0 and a successful status is returned. diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index 12616bc92e0..30c077fd0ed 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -1380,21 +1380,93 @@ impl FixedDecimal { /// assert_eq!("4", dec.to_string()); /// ``` pub fn half_trunc(&mut self, position: i16) { - let digit_after_position = self.digit_at_next_position(position); - let should_expand = match digit_after_position.cmp(&5) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => - // NOTE: `digit_after_position` equals 5, this means, position does not equal to `i16::MIN`. - { - self.nonzero_magnitude_end() < position - 1 + self.half_trunc_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Half Truncates the number on the right to a particular position and rounding increment, + /// deleting digits if necessary. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.half_trunc_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-4", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.half_trunc_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("5.45").unwrap(); + /// dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("5.50", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_trunc_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("10.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("9.98", dec.to_string()); + /// ``` + pub fn half_trunc_to_increment(&mut self, position: i16, increment: RoundingIncrement) { + fn next_digits_exceed_five(number: &mut FixedDecimal, position: i16) -> bool { + let digit_after_position = number.digit_at_next_position(position); + match digit_after_position.cmp(&5) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => + // NOTE: `digit_after_position` equals 5, this means that `position` + // does not equal `i16::MIN`. + { + number.nonzero_magnitude_end() < position - 1 + } + } + } + + let should_expand = match increment { + RoundingIncrement::MultiplesOf1 => next_digits_exceed_five(self, position), + RoundingIncrement::MultiplesOf2 => { + let current_digit = self.digit_at(position); + + // Equivalent to "if current_digit is odd". + if current_digit & 0x01 == 1 { + self.nonzero_magnitude_end() < position + } else { + // Even numbers are always below the threshold to expand. + false + } + } + RoundingIncrement::MultiplesOf5 => { + let current_digit = self.digit_at(position); + match (current_digit % 5).cmp(&2) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // Need to know if the number exceeds 2.5. + next_digits_exceed_five(self, position) + } + } + } + RoundingIncrement::MultiplesOf25 => { + let current_digit = self.digit_at(position); + let prev_digit = self.digit_at_previous_position(position); + let number = prev_digit * 10 + current_digit; + + match (number % 25).cmp(&12) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // Need to know if the number exceeds 12.5. + next_digits_exceed_five(self, position) + } + } } }; if should_expand { - self.expand(position); + self.expand_to_increment(position, increment); } else { - self.trunc(position); + self.trunc_to_increment(position, increment); } } @@ -1425,6 +1497,55 @@ impl FixedDecimal { self } + /// Half Truncates the number on the right to a particular position and rounding increment, + /// deleting digits if necessary. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-4", + /// dec.half_trunced_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.half_trunced_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("5.45").unwrap(); + /// assert_eq!( + /// "5.50", + /// dec.half_trunced_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.0", + /// dec.half_trunced_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "9.98", + /// dec.half_trunced_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn half_trunced_to_increment( + mut self, + position: i16, + increment: RoundingIncrement, + ) -> Self { + self.half_trunc_to_increment(position, increment); + self + } + /// Take the expand of the number at a particular position. /// /// # Examples @@ -1884,12 +2005,64 @@ impl FixedDecimal { /// assert_eq!("2", dec.to_string()); /// ``` pub fn half_expand(&mut self, position: i16) { - let digit_after_position = self.digit_at_next_position(position); + self.half_expand_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Take the half expand of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.half_expand_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-4", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.half_expand_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("5.45").unwrap(); + /// dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("5.50", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_expand_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("10.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("10.00", dec.to_string()); + /// ``` + pub fn half_expand_to_increment(&mut self, position: i16, increment: RoundingIncrement) { + let should_expand = match increment { + RoundingIncrement::MultiplesOf1 => self.digit_at_next_position(position) >= 5, + RoundingIncrement::MultiplesOf2 => self.digit_at(position) & 0x01 == 1, + RoundingIncrement::MultiplesOf5 => { + // Need to know if the number is greater or equal than 2.5. + let current_digit = self.digit_at(position); + match (current_digit % 5).cmp(&2) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => self.digit_at_next_position(position) >= 5, + } + } + RoundingIncrement::MultiplesOf25 => { + let current_digit = self.digit_at(position); + let prev_digit = self.digit_at_previous_position(position); + let number = prev_digit * 10 + current_digit; + + // Need to know if the number is greater or equal than 12.5. + match (number % 25).cmp(&12) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => self.digit_at_next_position(position) >= 5, + } + } + }; - if digit_after_position >= 5 { - self.expand(position); + if should_expand { + self.expand_to_increment(position, increment); } else { - self.trunc(position); + self.trunc_to_increment(position, increment); } } @@ -1917,6 +2090,54 @@ impl FixedDecimal { self } + /// Take the half expand of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-4", + /// dec.half_expanded_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.half_expanded_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("5.45").unwrap(); + /// assert_eq!( + /// "5.50", + /// dec.half_expanded_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.0", + /// dec.half_expanded_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.00", + /// dec.half_expanded_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn half_expanded_to_increment( + mut self, + position: i16, + increment: RoundingIncrement, + ) -> Self { + self.half_expand_to_increment(position, increment); + self + } + /// Take the ceiling of the number at a particular position. /// /// # Examples @@ -1942,12 +2163,40 @@ impl FixedDecimal { /// assert_eq!("2", dec.to_string()); /// ``` pub fn ceil(&mut self, position: i16) { + self.ceil_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Take the ceiling of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.ceil_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-2", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("8.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("-5.25", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.ceil_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("10.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-9.98", dec.to_string()); + /// ``` + pub fn ceil_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { - self.trunc(position); + self.trunc_to_increment(position, increment); return; } - self.expand(position); + self.expand_to_increment(position, increment); } /// Take the ceiling of the number at a particular position. @@ -1974,6 +2223,50 @@ impl FixedDecimal { self } + /// Take the ceiling of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-2", + /// dec.ceiled_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "8.0", + /// dec.ceiled_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// assert_eq!( + /// "-5.25", + /// dec.ceiled_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.0", + /// dec.ceiled_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// assert_eq!( + /// "-9.98", + /// dec.ceiled_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn ceiled_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { + self.ceil_to_increment(position, increment); + self + } + /// Take the half ceiling of the number at a particular position. /// /// # Examples @@ -1999,12 +2292,39 @@ impl FixedDecimal { /// assert_eq!("2", dec.to_string()); /// ``` pub fn half_ceil(&mut self, position: i16) { + self.half_ceil_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Take the half ceiling of the number at a particular position and rounding increment. + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.half_ceil_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-4", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("-5.50", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("10.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-9.98", dec.to_string()); + /// ``` + pub fn half_ceil_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { - self.half_trunc(position); + self.half_trunc_to_increment(position, increment); return; } - self.half_expand(position); + self.half_expand_to_increment(position, increment); } /// Take the half ceiling of the number at a particular position. @@ -2031,6 +2351,50 @@ impl FixedDecimal { self } + /// Take the half ceiling of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-4", + /// dec.half_ceiled_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.half_ceiled_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// assert_eq!( + /// "-5.50", + /// dec.half_ceiled_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.0", + /// dec.half_ceiled_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// assert_eq!( + /// "-9.98", + /// dec.half_ceiled_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn half_ceiled_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { + self.half_ceil_to_increment(position, increment); + self + } + /// Take the floor of the number at a particular position. /// /// # Examples @@ -2056,12 +2420,40 @@ impl FixedDecimal { /// assert_eq!("1", dec.to_string()); /// ``` pub fn floor(&mut self, position: i16) { + self.floor_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Take the floor of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.floor_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-4", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.floor_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("-5.50", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.floor_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-10.00", dec.to_string()); + /// ``` + pub fn floor_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { - self.expand(position); + self.expand_to_increment(position, increment); return; } - self.trunc(position); + self.trunc_to_increment(position, increment); } /// Take the floor of the number at a particular position. @@ -2088,7 +2480,51 @@ impl FixedDecimal { self } - /// Take the half floor of the number at a particular position. + /// Take the floor of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-4", + /// dec.floored_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.floored_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// assert_eq!( + /// "-5.50", + /// dec.floored_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.floored_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// assert_eq!( + /// "-10.00", + /// dec.floored_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn floored_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { + self.floor_to_increment(position, increment); + self + } + + /// Take the half floor of the number at a particular position. /// /// # Examples /// @@ -2113,12 +2549,40 @@ impl FixedDecimal { /// assert_eq!("1", dec.to_string()); /// ``` pub fn half_floor(&mut self, position: i16) { + self.half_floor_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Take the half floor of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.half_floor_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-4", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.half_floor_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("-5.50", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_floor_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("10.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-10.00", dec.to_string()); + /// ``` + pub fn half_floor_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { - self.half_expand(position); + self.half_expand_to_increment(position, increment); return; } - self.half_trunc(position); + self.half_trunc_to_increment(position, increment); } /// Take the half floor of the number at a particular position. @@ -2145,6 +2609,54 @@ impl FixedDecimal { self } + /// Take the half floor of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-4", + /// dec.half_floored_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.half_floored_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-5.45").unwrap(); + /// assert_eq!( + /// "-5.50", + /// dec.half_floored_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.0", + /// dec.half_floored_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("-9.99").unwrap(); + /// assert_eq!( + /// "-10.00", + /// dec.half_floored_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn half_floored_to_increment( + mut self, + position: i16, + increment: RoundingIncrement, + ) -> Self { + self.half_floor_to_increment(position, increment); + self + } + /// Take the half even of the number at a particular position. /// /// # Examples @@ -2170,24 +2682,130 @@ impl FixedDecimal { /// assert_eq!("2", dec.to_string()); /// ``` pub fn half_even(&mut self, position: i16) { - let digit_after_position = self.digit_at_next_position(position); - let should_expand = match digit_after_position.cmp(&5) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => { - // NOTE: `digit_after_position` equals to 5, this means that positon does not equal i16::MIN. - if self.nonzero_magnitude_end() < position - 1 { - true + self.half_even_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + /// Take the half even of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// dec.half_even_to_increment(0, RoundingIncrement::MultiplesOf2); + /// assert_eq!("-4", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// dec.half_even_to_increment(-1, RoundingIncrement::MultiplesOf5); + /// assert_eq!("7.5", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("5.45").unwrap(); + /// dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + /// assert_eq!("5.50", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_even_to_increment(-1, RoundingIncrement::MultiplesOf25); + /// assert_eq!("10.0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + /// assert_eq!("9.98", dec.to_string()); + /// ``` + pub fn half_even_to_increment(&mut self, position: i16, increment: RoundingIncrement) { + let should_expand = match increment { + RoundingIncrement::MultiplesOf1 => { + let digit_after_position = self.digit_at_next_position(position); + match digit_after_position.cmp(&5) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // NOTE: `digit_after_position` equals to 5, this means that `position` + // does not equal i16::MIN. + if self.nonzero_magnitude_end() < position - 1 { + true + } else { + self.digit_at(position) % 2 != 0 + } + } + } + } + RoundingIncrement::MultiplesOf2 => { + let current_digit = self.digit_at(position); + + // Equivalent to "if current_digit is odd". + if (current_digit & 0x01) == 1 { + if self.nonzero_magnitude_end() < position { + true + } else { + // This essentially expands to the "even" increments, + // or the increments that are in the even places on the + // rounding range: [0, 4, 8] + // Equivalent to `(current_digit / 2) is odd`. + (current_digit >> 1) & 0x01 == 1 + } } else { - self.digit_at(position) % 2 != 0 + // Even numbers are always below the threshold to expand. + false + } + } + RoundingIncrement::MultiplesOf5 => { + let current_digit = self.digit_at(position); + match (current_digit % 5).cmp(&2) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // Need to know if the number exceeds 2.5. + let digit_after_position = self.digit_at_next_position(position); + match digit_after_position.cmp(&5) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // NOTE: `digit_after_position` equals to 5, this means that `position` + // does not equal i16::MIN. + if self.nonzero_magnitude_end() < position - 1 { + true + } else { + // Expand 7.5 to 10 and truncate 2.5 to 0. + current_digit == 7 + } + } + } + } + } + } + RoundingIncrement::MultiplesOf25 => { + let current_digit = self.digit_at(position); + let prev_digit = self.digit_at_previous_position(position); + let full_number = prev_digit * 10 + current_digit; + + match (full_number % 25).cmp(&12) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // Need to know if the number exceeds 12.5. + let digit_after_position = self.digit_at_next_position(position); + match digit_after_position.cmp(&5) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // NOTE: `digit_after_position` equals to 5, this means that `position` + // does not equal i16::MIN. + if self.nonzero_magnitude_end() < position - 1 { + true + } else { + // Expand `37.5` to 50 and `87.5` to 100. + // Truncate `12.5` to 0 and `62.5` to 50. + full_number == 37 || full_number == 87 + } + } + } + } } } }; if should_expand { - self.expand(position); + self.expand_to_increment(position, increment); } else { - self.trunc(position); + self.trunc_to_increment(position, increment); } } @@ -2215,6 +2833,50 @@ impl FixedDecimal { self } + /// Take the half even of the number at a particular position and rounding increment. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::{FixedDecimal, RoundingIncrement}; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-3.5").unwrap(); + /// assert_eq!( + /// "-4", + /// dec.half_evened_to_increment(0, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("7.57").unwrap(); + /// assert_eq!( + /// "7.5", + /// dec.half_evened_to_increment(-1, RoundingIncrement::MultiplesOf5) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("5.45").unwrap(); + /// assert_eq!( + /// "5.50", + /// dec.half_evened_to_increment(-2, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "10.0", + /// dec.half_evened_to_increment(-1, RoundingIncrement::MultiplesOf25) + /// .to_string() + /// ); + /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); + /// assert_eq!( + /// "9.98", + /// dec.half_evened_to_increment(-2, RoundingIncrement::MultiplesOf2) + /// .to_string() + /// ); + /// ``` + pub fn half_evened_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { + self.half_even_to_increment(position, increment); + self + } + /// Concatenate another `FixedDecimal` into the end of this `FixedDecimal`. /// /// All nonzero digits in `other` must have lower magnitude than nonzero digits in `self`. @@ -4252,8 +4914,456 @@ fn test_rounding_increment() { dec.expand_to_increment(i16::MAX, RoundingIncrement::MultiplesOf25); assert_eq!(FixedDecimal::from(0).multiplied_pow10(i16::MAX), dec); - // Test specific cases + // Test Half Truncate Right + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.96", dec.to_string()); + + dec.half_trunc_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4236.0", dec.to_string()); + + dec.half_trunc_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4225", dec.to_string()); + + dec.half_trunc_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("00000", dec.to_string()); + + dec.half_trunc_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("00000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-100.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.half_trunc_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.6", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.half_trunc_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.half_trunc_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.700", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.half_trunc_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("0", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.half_trunc_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(6).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.half_trunc_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(10).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.half_trunc_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(75).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_trunc_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(6).multiplied_pow10(i16::MAX), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_trunc_to_increment(i16::MAX, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(5).multiplied_pow10(i16::MAX), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_trunc_to_increment(i16::MAX, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(0).multiplied_pow10(i16::MAX), dec); + + // Test Half Expand + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.98", dec.to_string()); + + dec.half_expand_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4236.0", dec.to_string()); + + dec.half_expand_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4225", dec.to_string()); + + dec.half_expand_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("00000", dec.to_string()); + + dec.half_expand_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("00000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-100.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.half_expand_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.6", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.half_expand_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.half_expand_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.700", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.half_expand_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("0", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.half_expand_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.half_expand_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(10).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.half_expand_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(75).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_expand_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MAX), dec); + + // Test Ceil + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.98", dec.to_string()); + + dec.ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4236.0", dec.to_string()); + + dec.ceil_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4250", dec.to_string()); + + dec.ceil_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("500000", dec.to_string()); + + dec.ceil_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("500000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-99.75", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.ceil_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.6", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.5", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.75", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.ceil_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.702", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.ceil_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("25", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.ceil_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.ceil_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(10).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.ceil_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(75).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.ceil_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MAX), dec); + + // Test Half Ceil + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.98", dec.to_string()); + + dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4236.0", dec.to_string()); + + dec.half_ceil_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4225", dec.to_string()); + + dec.half_ceil_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("00000", dec.to_string()); + + dec.half_ceil_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("00000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-100.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.6", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.half_ceil_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.700", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.half_ceil_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("0", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.half_ceil_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.half_ceil_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(10).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.half_ceil_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(75).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_ceil_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MAX), dec); + + // Test Floor + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.96", dec.to_string()); + + dec.floor_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4235.5", dec.to_string()); + + dec.floor_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4225", dec.to_string()); + + dec.floor_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("00000", dec.to_string()); + + dec.floor_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("00000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-100.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.floor_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.4", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.floor_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.25", dec.to_string()); + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.floor_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.700", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.floor_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("0", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.floor_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(6).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.floor_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(5).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.floor_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(50).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.floor_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(6).multiplied_pow10(i16::MAX), dec); + + // Test Half Floor + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.96", dec.to_string()); + + dec.half_floor_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4236.0", dec.to_string()); + + dec.half_floor_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4225", dec.to_string()); + + dec.half_floor_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("00000", dec.to_string()); + + dec.half_floor_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("00000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-100.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.half_floor_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.6", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.half_floor_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.half_floor_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.700", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.half_floor_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("0", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.half_floor_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(6).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.half_floor_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(10).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.half_floor_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(75).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_floor_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(6).multiplied_pow10(i16::MAX), dec); + + // Test Half Even + let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3); + assert_eq!("4235.970", dec.to_string()); + + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("4235.98", dec.to_string()); + + dec.half_even_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("4236.0", dec.to_string()); + + dec.half_even_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("4225", dec.to_string()); + + dec.half_even_to_increment(5, RoundingIncrement::MultiplesOf5); + assert_eq!("00000", dec.to_string()); + + dec.half_even_to_increment(2, RoundingIncrement::MultiplesOf2); + assert_eq!("00000", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-99.999").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-100.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1234.56").unwrap(); + dec.half_even_to_increment(-1, RoundingIncrement::MultiplesOf2); + assert_eq!("1234.6", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.009").unwrap(); + dec.half_even_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("0.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.60").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.40").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("0.7000000099").unwrap(); + dec.half_even_to_increment(-3, RoundingIncrement::MultiplesOf2); + assert_eq!("0.700", dec.to_string()); + + let mut dec = FixedDecimal::from_str("5").unwrap(); + dec.half_even_to_increment(0, RoundingIncrement::MultiplesOf25); + assert_eq!("0", dec.to_string()); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MIN); + dec.half_even_to_increment(i16::MIN, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(9).multiplied_pow10(i16::MIN); + dec.half_even_to_increment(i16::MIN, RoundingIncrement::MultiplesOf5); + assert_eq!(FixedDecimal::from(10).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(70).multiplied_pow10(i16::MIN); + dec.half_even_to_increment(i16::MIN, RoundingIncrement::MultiplesOf25); + assert_eq!(FixedDecimal::from(75).multiplied_pow10(i16::MIN), dec); + + let mut dec = FixedDecimal::from(7).multiplied_pow10(i16::MAX); + dec.half_even_to_increment(i16::MAX, RoundingIncrement::MultiplesOf2); + assert_eq!(FixedDecimal::from(8).multiplied_pow10(i16::MAX), dec); + + // Test specific cases let mut dec = FixedDecimal::from_str("1.108").unwrap(); dec.expand_to_increment(-2, RoundingIncrement::MultiplesOf2); assert_eq!("1.12", dec.to_string()); @@ -4369,4 +5479,68 @@ fn test_rounding_increment() { let mut dec = FixedDecimal::from_str("0.50").unwrap(); dec.expand_to_increment(-2, RoundingIncrement::MultiplesOf25); assert_eq!("0.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1.1025").unwrap(); + dec.half_trunc_to_increment(-3, RoundingIncrement::MultiplesOf5); + assert_eq!("1.100", dec.to_string()); + + let mut dec = FixedDecimal::from_str("1.10125").unwrap(); + dec.half_expand_to_increment(-4, RoundingIncrement::MultiplesOf25); + assert_eq!("1.1025", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-1.25").unwrap(); + dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("-1.0", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-1.251").unwrap(); + dec.half_ceil_to_increment(-1, RoundingIncrement::MultiplesOf5); + assert_eq!("-1.5", dec.to_string()); + + let mut dec = FixedDecimal::from_str("-1.125").unwrap(); + dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("-1.25", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.71").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.70", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.73").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.74", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.75").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.74", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.77").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.78", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.79").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.78", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.725").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf5); + assert_eq!("2.70", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.775").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf5); + assert_eq!("2.80", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.875").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("3.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.375").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("2.50", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.125").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("2.00", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.625").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf25); + assert_eq!("2.50", dec.to_string()); } From 5b1b4673b15f35bb2aeb8dccd11986b9ded382c8 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 2 Nov 2023 23:17:52 -0600 Subject: [PATCH 2/7] Extract common function and fix half even roundings --- utils/fixed_decimal/src/decimal.rs | 189 +++++++++++++++-------------- 1 file changed, 99 insertions(+), 90 deletions(-) diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index 30c077fd0ed..1ae1cb4ad25 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -349,6 +349,35 @@ impl FixedDecimal { } } + /// Returns the relative ordering of the digits at the right + /// of `magnitude` with respect to 5. + fn half_at_next_magnitude(&self, magnitude: i16) -> Ordering { + match self.digit_at_next_position(magnitude).cmp(&5) { + // If the next digit is equal to 5, we can know if we're at exactly 5 + // by comparing the next magnitude with the last nonzero magnitude of + // the number. + Ordering::Equal => match (magnitude - 1).cmp(&self.nonzero_magnitude_end()) { + Ordering::Greater => { + // `magnitude - 1` has non-zero digits at its right, + // meaning the number overall must be greater than 5. + Ordering::Greater + } + Ordering::Equal => { + // `magnitude - 1` is the last digit of the number, + // meaning the number is exactly 5. + Ordering::Equal + } + Ordering::Less => { + debug_assert!(false, "unreachable, `magnitude - 1` should not be zero"); + Ordering::Less + } + }, + // If the next digit is either greater or less than 5, + // we know that the digits cannot sum to exactly 5. + ord => ord, + } + } + /// Checks if this number is already rounded to the specified magnitude and /// increment. #[cfg(feature = "experimental")] @@ -1409,22 +1438,10 @@ impl FixedDecimal { /// assert_eq!("9.98", dec.to_string()); /// ``` pub fn half_trunc_to_increment(&mut self, position: i16, increment: RoundingIncrement) { - fn next_digits_exceed_five(number: &mut FixedDecimal, position: i16) -> bool { - let digit_after_position = number.digit_at_next_position(position); - match digit_after_position.cmp(&5) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => - // NOTE: `digit_after_position` equals 5, this means that `position` - // does not equal `i16::MIN`. - { - number.nonzero_magnitude_end() < position - 1 - } - } - } - let should_expand = match increment { - RoundingIncrement::MultiplesOf1 => next_digits_exceed_five(self, position), + RoundingIncrement::MultiplesOf1 => { + self.half_at_next_magnitude(position) == Ordering::Greater + } RoundingIncrement::MultiplesOf2 => { let current_digit = self.digit_at(position); @@ -1443,7 +1460,7 @@ impl FixedDecimal { Ordering::Greater => true, Ordering::Equal => { // Need to know if the number exceeds 2.5. - next_digits_exceed_five(self, position) + self.half_at_next_magnitude(position) == Ordering::Greater } } } @@ -1457,7 +1474,7 @@ impl FixedDecimal { Ordering::Greater => true, Ordering::Equal => { // Need to know if the number exceeds 12.5. - next_digits_exceed_five(self, position) + self.half_at_next_magnitude(position) == Ordering::Greater } } } @@ -2033,16 +2050,18 @@ impl FixedDecimal { /// assert_eq!("10.00", dec.to_string()); /// ``` pub fn half_expand_to_increment(&mut self, position: i16, increment: RoundingIncrement) { - let should_expand = match increment { - RoundingIncrement::MultiplesOf1 => self.digit_at_next_position(position) >= 5, - RoundingIncrement::MultiplesOf2 => self.digit_at(position) & 0x01 == 1, + let should_trunc = match increment { + RoundingIncrement::MultiplesOf1 => { + self.half_at_next_magnitude(position) == Ordering::Less + } + RoundingIncrement::MultiplesOf2 => self.digit_at(position) & 0x01 == 0, RoundingIncrement::MultiplesOf5 => { // Need to know if the number is greater or equal than 2.5. let current_digit = self.digit_at(position); match (current_digit % 5).cmp(&2) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => self.digit_at_next_position(position) >= 5, + Ordering::Less => true, + Ordering::Greater => false, + Ordering::Equal => self.half_at_next_magnitude(position) == Ordering::Less, } } RoundingIncrement::MultiplesOf25 => { @@ -2052,17 +2071,17 @@ impl FixedDecimal { // Need to know if the number is greater or equal than 12.5. match (number % 25).cmp(&12) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => self.digit_at_next_position(position) >= 5, + Ordering::Less => true, + Ordering::Greater => false, + Ordering::Equal => self.half_at_next_magnitude(position) == Ordering::Less, } } }; - if should_expand { - self.expand_to_increment(position, increment); - } else { + if should_trunc { self.trunc_to_increment(position, increment); + } else { + self.expand_to_increment(position, increment); } } @@ -2711,22 +2730,12 @@ impl FixedDecimal { /// ``` pub fn half_even_to_increment(&mut self, position: i16, increment: RoundingIncrement) { let should_expand = match increment { - RoundingIncrement::MultiplesOf1 => { - let digit_after_position = self.digit_at_next_position(position); - match digit_after_position.cmp(&5) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => { - // NOTE: `digit_after_position` equals to 5, this means that `position` - // does not equal i16::MIN. - if self.nonzero_magnitude_end() < position - 1 { - true - } else { - self.digit_at(position) % 2 != 0 - } - } - } - } + RoundingIncrement::MultiplesOf1 => match self.half_at_next_magnitude(position) { + Ordering::Less => false, + Ordering::Greater => true, + // Expand if odd, truncate if even. + Ordering::Equal => self.digit_at(position) & 0x01 == 1, + }, RoundingIncrement::MultiplesOf2 => { let current_digit = self.digit_at(position); @@ -2735,11 +2744,13 @@ impl FixedDecimal { if self.nonzero_magnitude_end() < position { true } else { + let previous_digit = self.digit_at_previous_position(position); + let full = previous_digit * 10 + current_digit; // This essentially expands to the "even" increments, // or the increments that are in the even places on the - // rounding range: [0, 4, 8] + // rounding range: [0, 4, 8, 12, 16, 20]. // Equivalent to `(current_digit / 2) is odd`. - (current_digit >> 1) & 0x01 == 1 + (full >> 1) & 0x01 == 1 } } else { // Even numbers are always below the threshold to expand. @@ -2751,24 +2762,13 @@ impl FixedDecimal { match (current_digit % 5).cmp(&2) { Ordering::Less => false, Ordering::Greater => true, - Ordering::Equal => { - // Need to know if the number exceeds 2.5. - let digit_after_position = self.digit_at_next_position(position); - match digit_after_position.cmp(&5) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => { - // NOTE: `digit_after_position` equals to 5, this means that `position` - // does not equal i16::MIN. - if self.nonzero_magnitude_end() < position - 1 { - true - } else { - // Expand 7.5 to 10 and truncate 2.5 to 0. - current_digit == 7 - } - } - } - } + // Need to know if the number exceeds 2.5. + Ordering::Equal => match self.half_at_next_magnitude(position) { + Ordering::Less => false, + Ordering::Greater => true, + // Expand 7.5 to 10 and truncate 2.5 to 0. + Ordering::Equal => current_digit == 7, + }, } } RoundingIncrement::MultiplesOf25 => { @@ -2779,25 +2779,14 @@ impl FixedDecimal { match (full_number % 25).cmp(&12) { Ordering::Less => false, Ordering::Greater => true, - Ordering::Equal => { - // Need to know if the number exceeds 12.5. - let digit_after_position = self.digit_at_next_position(position); - match digit_after_position.cmp(&5) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => { - // NOTE: `digit_after_position` equals to 5, this means that `position` - // does not equal i16::MIN. - if self.nonzero_magnitude_end() < position - 1 { - true - } else { - // Expand `37.5` to 50 and `87.5` to 100. - // Truncate `12.5` to 0 and `62.5` to 50. - full_number == 37 || full_number == 87 - } - } - } - } + // Need to know if the number exceeds 12.5. + Ordering::Equal => match self.half_at_next_magnitude(position) { + Ordering::Less => false, + Ordering::Greater => true, + // Expand `37.5` to 50 and `87.5` to 100. + // Truncate `12.5` to 0 and `62.5` to 50. + Ordering::Equal => full_number == 37 || full_number == 87, + }, } } }; @@ -5305,7 +5294,7 @@ fn test_rounding_increment() { assert_eq!("4235.970", dec.to_string()); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - assert_eq!("4235.98", dec.to_string()); + assert_eq!("4235.96", dec.to_string()); dec.half_even_to_increment(-1, RoundingIncrement::MultiplesOf5); assert_eq!("4236.0", dec.to_string()); @@ -5502,23 +5491,43 @@ fn test_rounding_increment() { let mut dec = FixedDecimal::from_str("2.71").unwrap(); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - assert_eq!("2.70", dec.to_string()); + assert_eq!("2.72", dec.to_string()); let mut dec = FixedDecimal::from_str("2.73").unwrap(); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - assert_eq!("2.74", dec.to_string()); + assert_eq!("2.72", dec.to_string()); let mut dec = FixedDecimal::from_str("2.75").unwrap(); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - assert_eq!("2.74", dec.to_string()); + assert_eq!("2.76", dec.to_string()); let mut dec = FixedDecimal::from_str("2.77").unwrap(); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - assert_eq!("2.78", dec.to_string()); + assert_eq!("2.76", dec.to_string()); let mut dec = FixedDecimal::from_str("2.79").unwrap(); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - assert_eq!("2.78", dec.to_string()); + assert_eq!("2.80", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.41").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.40", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.43").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.44", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.45").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.44", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.47").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.48", dec.to_string()); + + let mut dec = FixedDecimal::from_str("2.49").unwrap(); + dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); + assert_eq!("2.48", dec.to_string()); let mut dec = FixedDecimal::from_str("2.725").unwrap(); dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf5); From 0e564d57e31a143f334759c15bc173e3ccdc2efd Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Fri, 3 Nov 2023 01:08:27 -0600 Subject: [PATCH 3/7] Fix doc tests --- utils/fixed_decimal/src/decimal.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index 1ae1cb4ad25..e5b5fda4d66 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -2726,7 +2726,7 @@ impl FixedDecimal { /// assert_eq!("10.0", dec.to_string()); /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); /// dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); - /// assert_eq!("9.98", dec.to_string()); + /// assert_eq!("10.00", dec.to_string()); /// ``` pub fn half_even_to_increment(&mut self, position: i16, increment: RoundingIncrement) { let should_expand = match increment { @@ -2856,7 +2856,7 @@ impl FixedDecimal { /// ); /// let mut dec = FixedDecimal::from_str("9.99").unwrap(); /// assert_eq!( - /// "9.98", + /// "10.00", /// dec.half_evened_to_increment(-2, RoundingIncrement::MultiplesOf2) /// .to_string() /// ); From a7907ac3bd52b266406bd31222c475fc28368889 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sat, 4 Nov 2023 02:40:04 -0600 Subject: [PATCH 4/7] Reduce duplication on half roundings --- utils/fixed_decimal/src/decimal.rs | 239 ++++++++++++++--------------- 1 file changed, 111 insertions(+), 128 deletions(-) diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index e5b5fda4d66..bd45d5c88c6 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -349,8 +349,7 @@ impl FixedDecimal { } } - /// Returns the relative ordering of the digits at the right - /// of `magnitude` with respect to 5. + /// Returns the relative ordering of the digits after `magnitude` with respect to 5. fn half_at_next_magnitude(&self, magnitude: i16) -> Ordering { match self.digit_at_next_position(magnitude).cmp(&5) { // If the next digit is equal to 5, we can know if we're at exactly 5 @@ -368,7 +367,7 @@ impl FixedDecimal { Ordering::Equal } Ordering::Less => { - debug_assert!(false, "unreachable, `magnitude - 1` should not be zero"); + debug_assert!(false, "digit `magnitude - 1` should not be zero"); Ordering::Less } }, @@ -378,6 +377,72 @@ impl FixedDecimal { } } + /// Returns the relative ordering of the digits from `magnitude` onwards + /// with respect to the half increment of `increment`. + fn half_increment_at_magnitude( + &self, + magnitude: i16, + increment: RoundingIncrement, + ) -> Ordering { + match increment { + RoundingIncrement::MultiplesOf1 => self.half_at_next_magnitude(magnitude), + RoundingIncrement::MultiplesOf2 => { + let current_digit = self.digit_at(magnitude); + + // Equivalent to "if current_digit is odd". + if current_digit & 0x01 == 1 { + match magnitude.cmp(&self.nonzero_magnitude_end()) { + Ordering::Greater => { + // `magnitude` has non-zero digits at its right, + // meaning the number overall must be greater than + // the half increment. + Ordering::Greater + } + Ordering::Equal => { + // `magnitude` is the last digit of the number, + // meaning the number is exactly at a half increment. + Ordering::Equal + } + Ordering::Less => { + debug_assert!(false, "digit `magnitude` should not be zero"); + Ordering::Less + } + } + } else { + // Even numbers are always below the half increment. + Ordering::Less + } + } + RoundingIncrement::MultiplesOf5 => { + let current_digit = self.digit_at(magnitude); + match (current_digit % 5).cmp(&2) { + Ordering::Equal => { + // Need to know if the number exceeds 2.5. + self.half_at_next_magnitude(magnitude) + } + // If the next digit is either greater or less than the half increment, + // we know that the digits cannot sum to exactly it. + ord => ord, + } + } + RoundingIncrement::MultiplesOf25 => { + let current_digit = self.digit_at(magnitude); + let prev_digit = self.digit_at_previous_position(magnitude); + let number = prev_digit * 10 + current_digit; + + match (number % 25).cmp(&12) { + Ordering::Equal => { + // Need to know if the number exceeds 12.5. + self.half_at_next_magnitude(magnitude) + } + // If the next digit is either greater or less than the half increment, + // we know that the digits cannot sum to exactly it. + ord => ord, + } + } + } + } + /// Checks if this number is already rounded to the specified magnitude and /// increment. #[cfg(feature = "experimental")] @@ -1438,47 +1503,10 @@ impl FixedDecimal { /// assert_eq!("9.98", dec.to_string()); /// ``` pub fn half_trunc_to_increment(&mut self, position: i16, increment: RoundingIncrement) { - let should_expand = match increment { - RoundingIncrement::MultiplesOf1 => { - self.half_at_next_magnitude(position) == Ordering::Greater - } - RoundingIncrement::MultiplesOf2 => { - let current_digit = self.digit_at(position); - - // Equivalent to "if current_digit is odd". - if current_digit & 0x01 == 1 { - self.nonzero_magnitude_end() < position - } else { - // Even numbers are always below the threshold to expand. - false - } - } - RoundingIncrement::MultiplesOf5 => { - let current_digit = self.digit_at(position); - match (current_digit % 5).cmp(&2) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => { - // Need to know if the number exceeds 2.5. - self.half_at_next_magnitude(position) == Ordering::Greater - } - } - } - RoundingIncrement::MultiplesOf25 => { - let current_digit = self.digit_at(position); - let prev_digit = self.digit_at_previous_position(position); - let number = prev_digit * 10 + current_digit; - - match (number % 25).cmp(&12) { - Ordering::Less => false, - Ordering::Greater => true, - Ordering::Equal => { - // Need to know if the number exceeds 12.5. - self.half_at_next_magnitude(position) == Ordering::Greater - } - } - } - }; + // Only expand if the rounding position is strictly greater than the half increment. + // At the half increment, `half_trunc` always truncates. + let should_expand = + self.half_increment_at_magnitude(position, increment) == Ordering::Greater; if should_expand { self.expand_to_increment(position, increment); @@ -2050,33 +2078,9 @@ impl FixedDecimal { /// assert_eq!("10.00", dec.to_string()); /// ``` pub fn half_expand_to_increment(&mut self, position: i16, increment: RoundingIncrement) { - let should_trunc = match increment { - RoundingIncrement::MultiplesOf1 => { - self.half_at_next_magnitude(position) == Ordering::Less - } - RoundingIncrement::MultiplesOf2 => self.digit_at(position) & 0x01 == 0, - RoundingIncrement::MultiplesOf5 => { - // Need to know if the number is greater or equal than 2.5. - let current_digit = self.digit_at(position); - match (current_digit % 5).cmp(&2) { - Ordering::Less => true, - Ordering::Greater => false, - Ordering::Equal => self.half_at_next_magnitude(position) == Ordering::Less, - } - } - RoundingIncrement::MultiplesOf25 => { - let current_digit = self.digit_at(position); - let prev_digit = self.digit_at_previous_position(position); - let number = prev_digit * 10 + current_digit; - - // Need to know if the number is greater or equal than 12.5. - match (number % 25).cmp(&12) { - Ordering::Less => true, - Ordering::Greater => false, - Ordering::Equal => self.half_at_next_magnitude(position) == Ordering::Less, - } - } - }; + // Only truncate if the rounding position is strictly less than the half increment. + // At the half increment, `half_expand` always expands. + let should_trunc = self.half_increment_at_magnitude(position, increment) == Ordering::Less; if should_trunc { self.trunc_to_increment(position, increment); @@ -2729,66 +2733,45 @@ impl FixedDecimal { /// assert_eq!("10.00", dec.to_string()); /// ``` pub fn half_even_to_increment(&mut self, position: i16, increment: RoundingIncrement) { - let should_expand = match increment { - RoundingIncrement::MultiplesOf1 => match self.half_at_next_magnitude(position) { - Ordering::Less => false, - Ordering::Greater => true, - // Expand if odd, truncate if even. - Ordering::Equal => self.digit_at(position) & 0x01 == 1, - }, - RoundingIncrement::MultiplesOf2 => { - let current_digit = self.digit_at(position); - - // Equivalent to "if current_digit is odd". - if (current_digit & 0x01) == 1 { - if self.nonzero_magnitude_end() < position { - true - } else { - let previous_digit = self.digit_at_previous_position(position); - let full = previous_digit * 10 + current_digit; - // This essentially expands to the "even" increments, - // or the increments that are in the even places on the - // rounding range: [0, 4, 8, 12, 16, 20]. - // Equivalent to `(current_digit / 2) is odd`. - (full >> 1) & 0x01 == 1 - } - } else { - // Even numbers are always below the threshold to expand. - false + let should_expand = match self.half_increment_at_magnitude(position, increment) { + Ordering::Greater => true, + Ordering::Less => false, + Ordering::Equal => match increment { + RoundingIncrement::MultiplesOf1 => { + // Expand if odd, truncate if even. + self.digit_at(position) & 0x01 == 1 } - } - RoundingIncrement::MultiplesOf5 => { - let current_digit = self.digit_at(position); - match (current_digit % 5).cmp(&2) { - Ordering::Less => false, - Ordering::Greater => true, - // Need to know if the number exceeds 2.5. - Ordering::Equal => match self.half_at_next_magnitude(position) { - Ordering::Less => false, - Ordering::Greater => true, - // Expand 7.5 to 10 and truncate 2.5 to 0. - Ordering::Equal => current_digit == 7, - }, + RoundingIncrement::MultiplesOf2 => { + let current_digit = self.digit_at(position); + let previous_digit = self.digit_at_previous_position(position); + let full = previous_digit * 10 + current_digit; + + // This essentially expands to the "even" increments, + // or the increments that are in the even places on the + // rounding range: [0, 4, 8, 12, 16, 20, ...]. + // Equivalent to `(full / 2) is odd`. + // + // Examples: + // - 37 should truncate, since 37 % 20 = 17, which truncates to 16. + // 37 / 2 = 18, which is even. + // - 83 should expand, since 83 % 20 = 3, which expands to 4. + // 83 / 2 = 41, which is odd. + (full >> 1) & 0x01 == 1 } - } - RoundingIncrement::MultiplesOf25 => { - let current_digit = self.digit_at(position); - let prev_digit = self.digit_at_previous_position(position); - let full_number = prev_digit * 10 + current_digit; - - match (full_number % 25).cmp(&12) { - Ordering::Less => false, - Ordering::Greater => true, - // Need to know if the number exceeds 12.5. - Ordering::Equal => match self.half_at_next_magnitude(position) { - Ordering::Less => false, - Ordering::Greater => true, - // Expand `37.5` to 50 and `87.5` to 100. - // Truncate `12.5` to 0 and `62.5` to 50. - Ordering::Equal => full_number == 37 || full_number == 87, - }, + RoundingIncrement::MultiplesOf5 => { + // Expand 7.5 to 10 and truncate 2.5 to 0. + self.digit_at(position) == 7 } - } + RoundingIncrement::MultiplesOf25 => { + let current_digit = self.digit_at(position); + let prev_digit = self.digit_at_previous_position(position); + let full_number = prev_digit * 10 + current_digit; + + // Expand `37.5` to 50 and `87.5` to 100. + // Truncate `12.5` to 0 and `62.5` to 50. + full_number == 37 || full_number == 87 + } + }, }; if should_expand { From f103eccaaf3752253e802393c2523bf81bf8f3bd Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 9 Nov 2023 21:45:11 -0600 Subject: [PATCH 5/7] Gate methods behind experimental --- ffi/capi/c/include/ICU4XFixedDecimal.h | 14 - .../cpp/docs/source/fixed_decimal_ffi.rst | 35 -- ffi/capi/cpp/include/ICU4XFixedDecimal.h | 14 - ffi/capi/cpp/include/ICU4XFixedDecimal.hpp | 56 --- .../package/docs/source/fixed_decimal_ffi.rst | 35 -- .../js/package/lib/ICU4XFixedDecimal.d.ts | 42 -- ffi/capi/js/package/lib/ICU4XFixedDecimal.js | 28 -- ffi/capi/src/fixed_decimal.rs | 78 ---- tools/ffi_coverage/src/allowlist.rs | 14 + utils/fixed_decimal/src/decimal.rs | 374 ++++++++++++++++++ 10 files changed, 388 insertions(+), 302 deletions(-) diff --git a/ffi/capi/c/include/ICU4XFixedDecimal.h b/ffi/capi/c/include/ICU4XFixedDecimal.h index 42f91a75adf..361294b6874 100644 --- a/ffi/capi/c/include/ICU4XFixedDecimal.h +++ b/ffi/capi/c/include/ICU4XFixedDecimal.h @@ -75,34 +75,20 @@ void ICU4XFixedDecimal_trunc(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_trunc(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_trunc_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_expand(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_expand(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_expand_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_ceil(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_half_ceil(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_floor(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_half_floor(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_half_even(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_even_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - diplomat_result_void_void ICU4XFixedDecimal_concatenate_end(ICU4XFixedDecimal* self, ICU4XFixedDecimal* other); void ICU4XFixedDecimal_to_string(const ICU4XFixedDecimal* self, DiplomatWriteable* to); diff --git a/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst b/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst index eede7eb7106..7adcddfc75d 100644 --- a/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst +++ b/ffi/capi/cpp/docs/source/fixed_decimal_ffi.rst @@ -172,11 +172,6 @@ See the `Rust documentation for half_trunc `__ for more information. - .. cpp:function:: void half_trunc_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for half_trunc_to_increment `__ for more information. - - .. cpp:function:: void expand(int16_t position) See the `Rust documentation for expand `__ for more information. @@ -187,61 +182,31 @@ See the `Rust documentation for half_expand `__ for more information. - .. cpp:function:: void half_expand_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for half_expand_to_increment `__ for more information. - - .. cpp:function:: void ceil(int16_t position) See the `Rust documentation for ceil `__ for more information. - .. cpp:function:: void ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for ceil_to_increment `__ for more information. - - .. cpp:function:: void half_ceil(int16_t position) See the `Rust documentation for half_ceil `__ for more information. - .. cpp:function:: void half_ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for half_ceil_to_increment `__ for more information. - - .. cpp:function:: void floor(int16_t position) See the `Rust documentation for floor `__ for more information. - .. cpp:function:: void floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for floor_to_increment `__ for more information. - - .. cpp:function:: void half_floor(int16_t position) See the `Rust documentation for half_floor `__ for more information. - .. cpp:function:: void half_floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for half_floor_to_increment `__ for more information. - - .. cpp:function:: void half_even(int16_t position) See the `Rust documentation for half_even `__ for more information. - .. cpp:function:: void half_even_to_increment(int16_t position, ICU4XRoundingIncrement increment) - - See the `Rust documentation for half_even_to_increment `__ for more information. - - .. cpp:function:: diplomat::result concatenate_end(ICU4XFixedDecimal& other) Concatenates ``other`` to the end of ``self``. diff --git a/ffi/capi/cpp/include/ICU4XFixedDecimal.h b/ffi/capi/cpp/include/ICU4XFixedDecimal.h index 42f91a75adf..361294b6874 100644 --- a/ffi/capi/cpp/include/ICU4XFixedDecimal.h +++ b/ffi/capi/cpp/include/ICU4XFixedDecimal.h @@ -75,34 +75,20 @@ void ICU4XFixedDecimal_trunc(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_trunc(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_trunc_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_expand(ICU4XFixedDecimal* self, int16_t position); void ICU4XFixedDecimal_half_expand(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_expand_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_ceil(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_half_ceil(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_ceil_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_floor(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_half_floor(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_floor_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - void ICU4XFixedDecimal_half_even(ICU4XFixedDecimal* self, int16_t position); -void ICU4XFixedDecimal_half_even_to_increment(ICU4XFixedDecimal* self, int16_t position, ICU4XRoundingIncrement increment); - diplomat_result_void_void ICU4XFixedDecimal_concatenate_end(ICU4XFixedDecimal* self, ICU4XFixedDecimal* other); void ICU4XFixedDecimal_to_string(const ICU4XFixedDecimal* self, DiplomatWriteable* to); diff --git a/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp b/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp index 740f00cf8c4..fed2ac629a8 100644 --- a/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp +++ b/ffi/capi/cpp/include/ICU4XFixedDecimal.hpp @@ -199,11 +199,6 @@ class ICU4XFixedDecimal { */ void half_trunc(int16_t position); - /** - * See the [Rust documentation for `half_trunc_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_trunc_to_increment) for more information. - */ - void half_trunc_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * See the [Rust documentation for `expand`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.expand) for more information. */ @@ -214,61 +209,31 @@ class ICU4XFixedDecimal { */ void half_expand(int16_t position); - /** - * See the [Rust documentation for `half_expand_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_expand_to_increment) for more information. - */ - void half_expand_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * See the [Rust documentation for `ceil`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil) for more information. */ void ceil(int16_t position); - /** - * See the [Rust documentation for `ceil_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil_to_increment) for more information. - */ - void ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * See the [Rust documentation for `half_ceil`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil) for more information. */ void half_ceil(int16_t position); - /** - * See the [Rust documentation for `half_ceil_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil_to_increment) for more information. - */ - void half_ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * See the [Rust documentation for `floor`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor) for more information. */ void floor(int16_t position); - /** - * See the [Rust documentation for `floor_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor_to_increment) for more information. - */ - void floor_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * See the [Rust documentation for `half_floor`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor) for more information. */ void half_floor(int16_t position); - /** - * See the [Rust documentation for `half_floor_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor_to_increment) for more information. - */ - void half_floor_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * See the [Rust documentation for `half_even`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even) for more information. */ void half_even(int16_t position); - /** - * See the [Rust documentation for `half_even_to_increment`](https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even_to_increment) for more information. - */ - void half_even_to_increment(int16_t position, ICU4XRoundingIncrement increment); - /** * Concatenates `other` to the end of `self`. * @@ -417,48 +382,27 @@ inline void ICU4XFixedDecimal::trunc(int16_t position) { inline void ICU4XFixedDecimal::half_trunc(int16_t position) { capi::ICU4XFixedDecimal_half_trunc(this->inner.get(), position); } -inline void ICU4XFixedDecimal::half_trunc_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_half_trunc_to_increment(this->inner.get(), position, static_cast(increment)); -} inline void ICU4XFixedDecimal::expand(int16_t position) { capi::ICU4XFixedDecimal_expand(this->inner.get(), position); } inline void ICU4XFixedDecimal::half_expand(int16_t position) { capi::ICU4XFixedDecimal_half_expand(this->inner.get(), position); } -inline void ICU4XFixedDecimal::half_expand_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_half_expand_to_increment(this->inner.get(), position, static_cast(increment)); -} inline void ICU4XFixedDecimal::ceil(int16_t position) { capi::ICU4XFixedDecimal_ceil(this->inner.get(), position); } -inline void ICU4XFixedDecimal::ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_ceil_to_increment(this->inner.get(), position, static_cast(increment)); -} inline void ICU4XFixedDecimal::half_ceil(int16_t position) { capi::ICU4XFixedDecimal_half_ceil(this->inner.get(), position); } -inline void ICU4XFixedDecimal::half_ceil_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_half_ceil_to_increment(this->inner.get(), position, static_cast(increment)); -} inline void ICU4XFixedDecimal::floor(int16_t position) { capi::ICU4XFixedDecimal_floor(this->inner.get(), position); } -inline void ICU4XFixedDecimal::floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_floor_to_increment(this->inner.get(), position, static_cast(increment)); -} inline void ICU4XFixedDecimal::half_floor(int16_t position) { capi::ICU4XFixedDecimal_half_floor(this->inner.get(), position); } -inline void ICU4XFixedDecimal::half_floor_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_half_floor_to_increment(this->inner.get(), position, static_cast(increment)); -} inline void ICU4XFixedDecimal::half_even(int16_t position) { capi::ICU4XFixedDecimal_half_even(this->inner.get(), position); } -inline void ICU4XFixedDecimal::half_even_to_increment(int16_t position, ICU4XRoundingIncrement increment) { - capi::ICU4XFixedDecimal_half_even_to_increment(this->inner.get(), position, static_cast(increment)); -} inline diplomat::result ICU4XFixedDecimal::concatenate_end(ICU4XFixedDecimal& other) { auto diplomat_result_raw_out_value = capi::ICU4XFixedDecimal_concatenate_end(this->inner.get(), other.AsFFIMut()); diplomat::result diplomat_result_out_value; diff --git a/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst b/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst index ecc622cdf0b..354705dc5cc 100644 --- a/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst +++ b/ffi/capi/js/package/docs/source/fixed_decimal_ffi.rst @@ -172,11 +172,6 @@ See the `Rust documentation for half_trunc `__ for more information. - .. js:method:: half_trunc_to_increment(position, increment) - - See the `Rust documentation for half_trunc_to_increment `__ for more information. - - .. js:method:: expand(position) See the `Rust documentation for expand `__ for more information. @@ -187,61 +182,31 @@ See the `Rust documentation for half_expand `__ for more information. - .. js:method:: half_expand_to_increment(position, increment) - - See the `Rust documentation for half_expand_to_increment `__ for more information. - - .. js:method:: ceil(position) See the `Rust documentation for ceil `__ for more information. - .. js:method:: ceil_to_increment(position, increment) - - See the `Rust documentation for ceil_to_increment `__ for more information. - - .. js:method:: half_ceil(position) See the `Rust documentation for half_ceil `__ for more information. - .. js:method:: half_ceil_to_increment(position, increment) - - See the `Rust documentation for half_ceil_to_increment `__ for more information. - - .. js:method:: floor(position) See the `Rust documentation for floor `__ for more information. - .. js:method:: floor_to_increment(position, increment) - - See the `Rust documentation for floor_to_increment `__ for more information. - - .. js:method:: half_floor(position) See the `Rust documentation for half_floor `__ for more information. - .. js:method:: half_floor_to_increment(position, increment) - - See the `Rust documentation for half_floor_to_increment `__ for more information. - - .. js:method:: half_even(position) See the `Rust documentation for half_even `__ for more information. - .. js:method:: half_even_to_increment(position, increment) - - See the `Rust documentation for half_even_to_increment `__ for more information. - - .. js:method:: concatenate_end(other) Concatenates ``other`` to the end of ``self``. diff --git a/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts b/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts index 93aa704fbd0..dfa978cbfd4 100644 --- a/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts +++ b/ffi/capi/js/package/lib/ICU4XFixedDecimal.d.ts @@ -207,12 +207,6 @@ export class ICU4XFixedDecimal { */ half_trunc(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_trunc_to_increment Rust documentation for `half_trunc_to_increment`} for more information. - */ - half_trunc_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.expand Rust documentation for `expand`} for more information. @@ -225,72 +219,36 @@ export class ICU4XFixedDecimal { */ half_expand(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_expand_to_increment Rust documentation for `half_expand_to_increment`} for more information. - */ - half_expand_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil Rust documentation for `ceil`} for more information. */ ceil(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.ceil_to_increment Rust documentation for `ceil_to_increment`} for more information. - */ - ceil_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil Rust documentation for `half_ceil`} for more information. */ half_ceil(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_ceil_to_increment Rust documentation for `half_ceil_to_increment`} for more information. - */ - half_ceil_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor Rust documentation for `floor`} for more information. */ floor(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.floor_to_increment Rust documentation for `floor_to_increment`} for more information. - */ - floor_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor Rust documentation for `half_floor`} for more information. */ half_floor(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_floor_to_increment Rust documentation for `half_floor_to_increment`} for more information. - */ - half_floor_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even Rust documentation for `half_even`} for more information. */ half_even(position: i16): void; - /** - - * See the {@link https://docs.rs/fixed_decimal/latest/fixed_decimal/struct.FixedDecimal.html#method.half_even_to_increment Rust documentation for `half_even_to_increment`} for more information. - */ - half_even_to_increment(position: i16, increment: ICU4XRoundingIncrement): void; - /** * Concatenates `other` to the end of `self`. diff --git a/ffi/capi/js/package/lib/ICU4XFixedDecimal.js b/ffi/capi/js/package/lib/ICU4XFixedDecimal.js index 8db2542a720..e517bc9f48c 100644 --- a/ffi/capi/js/package/lib/ICU4XFixedDecimal.js +++ b/ffi/capi/js/package/lib/ICU4XFixedDecimal.js @@ -190,10 +190,6 @@ export class ICU4XFixedDecimal { wasm.ICU4XFixedDecimal_half_trunc(this.underlying, arg_position); } - half_trunc_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_half_trunc_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - expand(arg_position) { wasm.ICU4XFixedDecimal_expand(this.underlying, arg_position); } @@ -202,50 +198,26 @@ export class ICU4XFixedDecimal { wasm.ICU4XFixedDecimal_half_expand(this.underlying, arg_position); } - half_expand_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_half_expand_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - ceil(arg_position) { wasm.ICU4XFixedDecimal_ceil(this.underlying, arg_position); } - ceil_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_ceil_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - half_ceil(arg_position) { wasm.ICU4XFixedDecimal_half_ceil(this.underlying, arg_position); } - half_ceil_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_half_ceil_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - floor(arg_position) { wasm.ICU4XFixedDecimal_floor(this.underlying, arg_position); } - floor_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_floor_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - half_floor(arg_position) { wasm.ICU4XFixedDecimal_half_floor(this.underlying, arg_position); } - half_floor_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_half_floor_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - half_even(arg_position) { wasm.ICU4XFixedDecimal_half_even(this.underlying, arg_position); } - half_even_to_increment(arg_position, arg_increment) { - wasm.ICU4XFixedDecimal_half_even_to_increment(this.underlying, arg_position, ICU4XRoundingIncrement_js_to_rust[arg_increment]); - } - concatenate_end(arg_other) { return (() => { const is_ok = wasm.ICU4XFixedDecimal_concatenate_end(this.underlying, arg_other.underlying) == 1; diff --git a/ffi/capi/src/fixed_decimal.rs b/ffi/capi/src/fixed_decimal.rs index 839705f8ea2..64396983236 100644 --- a/ffi/capi/src/fixed_decimal.rs +++ b/ffi/capi/src/fixed_decimal.rs @@ -244,20 +244,6 @@ pub mod ffi { self.0.half_trunc(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_trunc_to_increment, FnInStruct)] - #[diplomat::rust_link( - fixed_decimal::FixedDecimal::half_trunced_to_increment, - FnInStruct, - hidden - )] - pub fn half_trunc_to_increment( - &mut self, - position: i16, - increment: ICU4XRoundingIncrement, - ) { - self.0.half_trunc_to_increment(position, increment.into()) - } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::expand, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::expanded, FnInStruct, hidden)] pub fn expand(&mut self, position: i16) { @@ -270,100 +256,36 @@ pub mod ffi { self.0.half_expand(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_expand_to_increment, FnInStruct)] - #[diplomat::rust_link( - fixed_decimal::FixedDecimal::half_expanded_to_increment, - FnInStruct, - hidden - )] - pub fn half_expand_to_increment( - &mut self, - position: i16, - increment: ICU4XRoundingIncrement, - ) { - self.0.half_expand_to_increment(position, increment.into()) - } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceil, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceiled, FnInStruct, hidden)] pub fn ceil(&mut self, position: i16) { self.0.ceil(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceil_to_increment, FnInStruct)] - #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceiled_to_increment, FnInStruct, hidden)] - pub fn ceil_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { - self.0.ceil_to_increment(position, increment.into()) - } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceil, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceiled, FnInStruct, hidden)] pub fn half_ceil(&mut self, position: i16) { self.0.half_ceil(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceil_to_increment, FnInStruct)] - #[diplomat::rust_link( - fixed_decimal::FixedDecimal::half_ceiled_to_increment, - FnInStruct, - hidden - )] - pub fn half_ceil_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { - self.0.half_ceil_to_increment(position, increment.into()) - } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::floor, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::floored, FnInStruct, hidden)] pub fn floor(&mut self, position: i16) { self.0.floor(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::floor_to_increment, FnInStruct)] - #[diplomat::rust_link( - fixed_decimal::FixedDecimal::floored_to_increment, - FnInStruct, - hidden - )] - pub fn floor_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { - self.0.floor_to_increment(position, increment.into()) - } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floor, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floored, FnInStruct, hidden)] pub fn half_floor(&mut self, position: i16) { self.0.half_floor(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floor_to_increment, FnInStruct)] - #[diplomat::rust_link( - fixed_decimal::FixedDecimal::half_floored_to_increment, - FnInStruct, - hidden - )] - pub fn half_floor_to_increment( - &mut self, - position: i16, - increment: ICU4XRoundingIncrement, - ) { - self.0.half_floor_to_increment(position, increment.into()) - } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_even, FnInStruct)] #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_evened, FnInStruct, hidden)] pub fn half_even(&mut self, position: i16) { self.0.half_even(position) } - #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_even_to_increment, FnInStruct)] - #[diplomat::rust_link( - fixed_decimal::FixedDecimal::half_evened_to_increment, - FnInStruct, - hidden - )] - pub fn half_even_to_increment(&mut self, position: i16, increment: ICU4XRoundingIncrement) { - self.0.half_even_to_increment(position, increment.into()) - } - /// Concatenates `other` to the end of `self`. /// /// If successful, `other` will be set to 0 and a successful status is returned. diff --git a/tools/ffi_coverage/src/allowlist.rs b/tools/ffi_coverage/src/allowlist.rs index 53a396adbd6..6d7a4b17680 100644 --- a/tools/ffi_coverage/src/allowlist.rs +++ b/tools/ffi_coverage/src/allowlist.rs @@ -317,6 +317,20 @@ lazy_static::lazy_static! { "fixed_decimal::FixedDecimal::expanded_to_increment", "fixed_decimal::FixedDecimal::trunc_to_increment", "fixed_decimal::FixedDecimal::trunced_to_increment", + "fixed_decimal::FixedDecimal::ceil_to_increment", + "fixed_decimal::FixedDecimal::ceiled_to_increment", + "fixed_decimal::FixedDecimal::floor_to_increment", + "fixed_decimal::FixedDecimal::floored_to_increment", + "fixed_decimal::FixedDecimal::half_ceil_to_increment", + "fixed_decimal::FixedDecimal::half_ceiled_to_increment", + "fixed_decimal::FixedDecimal::half_even_to_increment", + "fixed_decimal::FixedDecimal::half_evened_to_increment", + "fixed_decimal::FixedDecimal::half_expand_to_increment", + "fixed_decimal::FixedDecimal::half_expanded_to_increment", + "fixed_decimal::FixedDecimal::half_floor_to_increment", + "fixed_decimal::FixedDecimal::half_floored_to_increment", + "fixed_decimal::FixedDecimal::half_trunc_to_increment", + "fixed_decimal::FixedDecimal::half_trunced_to_increment", // Stuff that does not need to be exposed over FFI // Especially for stuff that are Rust specific like conversion traits diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index bd45d5c88c6..181ef117117 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -350,6 +350,7 @@ impl FixedDecimal { } /// Returns the relative ordering of the digits after `magnitude` with respect to 5. + #[cfg(feature = "experimental")] fn half_at_next_magnitude(&self, magnitude: i16) -> Ordering { match self.digit_at_next_position(magnitude).cmp(&5) { // If the next digit is equal to 5, we can know if we're at exactly 5 @@ -379,6 +380,7 @@ impl FixedDecimal { /// Returns the relative ordering of the digits from `magnitude` onwards /// with respect to the half increment of `increment`. + #[cfg(feature = "experimental")] fn half_increment_at_magnitude( &self, magnitude: i16, @@ -1473,6 +1475,55 @@ impl FixedDecimal { /// dec.half_trunc(0); /// assert_eq!("4", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn half_trunc(&mut self, position: i16) { + let digit_after_position = self.digit_at_next_position(position); + let should_expand = match digit_after_position.cmp(&5) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => + // NOTE: `digit_after_position` equals 5, this means, position does not equal to `i16::MIN`. + { + self.nonzero_magnitude_end() < position - 1 + } + }; + + if should_expand { + self.expand(position); + } else { + self.trunc(position); + } + } + + /// Half Truncates the number on the right to a particular position, deleting + /// digits if necessary. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.half_trunc(0); + /// assert_eq!("-1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.half_trunc(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.half_trunc(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.half_trunc(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.half_trunc(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("3.954").unwrap(); + /// dec.half_trunc(0); + /// assert_eq!("4", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn half_trunc(&mut self, position: i16) { self.half_trunc_to_increment(position, RoundingIncrement::MultiplesOf1); } @@ -1480,6 +1531,12 @@ impl FixedDecimal { /// Half Truncates the number on the right to a particular position and rounding increment, /// deleting digits if necessary. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -1502,6 +1559,7 @@ impl FixedDecimal { /// dec.half_trunc_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("9.98", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn half_trunc_to_increment(&mut self, position: i16, increment: RoundingIncrement) { // Only expand if the rounding position is strictly greater than the half increment. // At the half increment, `half_trunc` always truncates. @@ -1545,6 +1603,12 @@ impl FixedDecimal { /// Half Truncates the number on the right to a particular position and rounding increment, /// deleting digits if necessary. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -1582,6 +1646,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn half_trunced_to_increment( mut self, position: i16, @@ -2049,12 +2114,54 @@ impl FixedDecimal { /// dec.half_expand(0); /// assert_eq!("2", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn half_expand(&mut self, position: i16) { + let digit_after_position = self.digit_at_next_position(position); + + if digit_after_position >= 5 { + self.expand(position); + } else { + self.trunc(position); + } + } + + /// Take the half expand of the number at a particular position. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.half_expand(0); + /// assert_eq!("-2", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.half_expand(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.half_expand(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.half_expand(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.half_expand(0); + /// assert_eq!("2", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn half_expand(&mut self, position: i16) { self.half_expand_to_increment(position, RoundingIncrement::MultiplesOf1); } /// Take the half expand of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2077,6 +2184,7 @@ impl FixedDecimal { /// dec.half_expand_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("10.00", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn half_expand_to_increment(&mut self, position: i16, increment: RoundingIncrement) { // Only truncate if the rounding position is strictly less than the half increment. // At the half increment, `half_expand` always expands. @@ -2115,6 +2223,12 @@ impl FixedDecimal { /// Take the half expand of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2152,6 +2266,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn half_expanded_to_increment( mut self, position: i16, @@ -2185,12 +2300,53 @@ impl FixedDecimal { /// dec.ceil(0); /// assert_eq!("2", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn ceil(&mut self, position: i16) { + if self.sign == Sign::Negative { + self.trunc(position); + return; + } + + self.expand(position); + } + + /// Take the ceiling of the number at a particular position. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.ceil(0); + /// assert_eq!("-1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.ceil(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.ceil(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.ceil(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.ceil(0); + /// assert_eq!("2", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn ceil(&mut self, position: i16) { self.ceil_to_increment(position, RoundingIncrement::MultiplesOf1); } /// Take the ceiling of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2213,6 +2369,7 @@ impl FixedDecimal { /// dec.ceil_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("-9.98", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn ceil_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { self.trunc_to_increment(position, increment); @@ -2248,6 +2405,12 @@ impl FixedDecimal { /// Take the ceiling of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2285,6 +2448,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn ceiled_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { self.ceil_to_increment(position, increment); self @@ -2314,11 +2478,53 @@ impl FixedDecimal { /// dec.half_ceil(0); /// assert_eq!("2", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn half_ceil(&mut self, position: i16) { + if self.sign == Sign::Negative { + self.half_trunc(position); + return; + } + + self.half_expand(position); + } + + /// Take the half ceiling of the number at a particular position. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.half_ceil(0); + /// assert_eq!("-1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.half_ceil(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.half_ceil(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.half_ceil(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.half_ceil(0); + /// assert_eq!("2", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn half_ceil(&mut self, position: i16) { self.half_ceil_to_increment(position, RoundingIncrement::MultiplesOf1); } /// Take the half ceiling of the number at a particular position and rounding increment. + /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2341,6 +2547,7 @@ impl FixedDecimal { /// dec.half_ceil_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("-9.98", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn half_ceil_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { self.half_trunc_to_increment(position, increment); @@ -2376,6 +2583,12 @@ impl FixedDecimal { /// Take the half ceiling of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2413,6 +2626,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn half_ceiled_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { self.half_ceil_to_increment(position, increment); self @@ -2442,12 +2656,53 @@ impl FixedDecimal { /// dec.floor(0); /// assert_eq!("1", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn floor(&mut self, position: i16) { + if self.sign == Sign::Negative { + self.expand(position); + return; + } + + self.trunc(position); + } + + /// Take the floor of the number at a particular position. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.floor(0); + /// assert_eq!("-2", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.floor(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.floor(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.floor(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.floor(0); + /// assert_eq!("1", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn floor(&mut self, position: i16) { self.floor_to_increment(position, RoundingIncrement::MultiplesOf1); } /// Take the floor of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2470,6 +2725,7 @@ impl FixedDecimal { /// dec.floor_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("-10.00", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn floor_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { self.expand_to_increment(position, increment); @@ -2505,6 +2761,12 @@ impl FixedDecimal { /// Take the floor of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2542,6 +2804,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn floored_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { self.floor_to_increment(position, increment); self @@ -2571,12 +2834,53 @@ impl FixedDecimal { /// dec.half_floor(0); /// assert_eq!("1", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn half_floor(&mut self, position: i16) { + if self.sign == Sign::Negative { + self.half_expand(position); + return; + } + + self.half_trunc(position); + } + + /// Take the half floor of the number at a particular position. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.half_floor(0); + /// assert_eq!("-2", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.half_floor(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.half_floor(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.half_floor(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.half_floor(0); + /// assert_eq!("1", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn half_floor(&mut self, position: i16) { self.half_floor_to_increment(position, RoundingIncrement::MultiplesOf1); } /// Take the half floor of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2599,6 +2903,7 @@ impl FixedDecimal { /// dec.half_floor_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("-10.00", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn half_floor_to_increment(&mut self, position: i16, increment: RoundingIncrement) { if self.sign == Sign::Negative { self.half_expand_to_increment(position, increment); @@ -2634,6 +2939,12 @@ impl FixedDecimal { /// Take the half floor of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2671,6 +2982,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn half_floored_to_increment( mut self, position: i16, @@ -2704,12 +3016,66 @@ impl FixedDecimal { /// dec.half_even(0); /// assert_eq!("2", dec.to_string()); /// ``` + #[cfg(not(feature = "experimental"))] + pub fn half_even(&mut self, position: i16) { + let digit_after_position = self.digit_at_next_position(position); + let should_expand = match digit_after_position.cmp(&5) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + // NOTE: `digit_after_position` equals to 5, this means that positon does not equal i16::MIN. + if self.nonzero_magnitude_end() < position - 1 { + true + } else { + self.digit_at(position) % 2 != 0 + } + } + }; + + if should_expand { + self.expand(position); + } else { + self.trunc(position); + } + } + + /// Take the half even of the number at a particular position. + /// + /// # Examples + /// + /// ``` + /// use fixed_decimal::FixedDecimal; + /// # use std::str::FromStr; + /// + /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); + /// dec.half_even(0); + /// assert_eq!("-2", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); + /// dec.half_even(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); + /// dec.half_even(0); + /// assert_eq!("0", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); + /// dec.half_even(0); + /// assert_eq!("1", dec.to_string()); + /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); + /// dec.half_even(0); + /// assert_eq!("2", dec.to_string()); + /// ``` + #[cfg(feature = "experimental")] pub fn half_even(&mut self, position: i16) { self.half_even_to_increment(position, RoundingIncrement::MultiplesOf1); } /// Take the half even of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2732,6 +3098,7 @@ impl FixedDecimal { /// dec.half_even_to_increment(-2, RoundingIncrement::MultiplesOf2); /// assert_eq!("10.00", dec.to_string()); /// ``` + #[cfg(feature = "experimental")] pub fn half_even_to_increment(&mut self, position: i16, increment: RoundingIncrement) { let should_expand = match self.half_increment_at_magnitude(position, increment) { Ordering::Greater => true, @@ -2807,6 +3174,12 @@ impl FixedDecimal { /// Take the half even of the number at a particular position and rounding increment. /// + ///
+ /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, + /// including in SemVer minor releases. Use with caution. + /// #3929 + ///
+ /// /// # Examples /// /// ``` @@ -2844,6 +3217,7 @@ impl FixedDecimal { /// .to_string() /// ); /// ``` + #[cfg(feature = "experimental")] pub fn half_evened_to_increment(mut self, position: i16, increment: RoundingIncrement) -> Self { self.half_even_to_increment(position, increment); self From 97e43bbb54da3da855227643cde44d2ec17bfd05 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 15 Nov 2023 15:32:42 -0600 Subject: [PATCH 6/7] Add data-driven test and debug checks --- utils/fixed_decimal/src/decimal.rs | 311 ++++++++------------------ utils/fixed_decimal/tests/rounding.rs | 87 +++++++ 2 files changed, 184 insertions(+), 214 deletions(-) diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index 181ef117117..fc791b0e6c4 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -352,6 +352,9 @@ impl FixedDecimal { /// Returns the relative ordering of the digits after `magnitude` with respect to 5. #[cfg(feature = "experimental")] fn half_at_next_magnitude(&self, magnitude: i16) -> Ordering { + #[cfg(debug_assertions)] // Depends on having no trailing zeroes. + self.check_invariants(); + match self.digit_at_next_position(magnitude).cmp(&5) { // If the next digit is equal to 5, we can know if we're at exactly 5 // by comparing the next magnitude with the last nonzero magnitude of @@ -386,6 +389,9 @@ impl FixedDecimal { magnitude: i16, increment: RoundingIncrement, ) -> Ordering { + #[cfg(debug_assertions)] // Depends on having no trailing zeroes. + self.check_invariants(); + match increment { RoundingIncrement::MultiplesOf1 => self.half_at_next_magnitude(magnitude), RoundingIncrement::MultiplesOf2 => { @@ -1475,8 +1481,20 @@ impl FixedDecimal { /// dec.half_trunc(0); /// assert_eq!("4", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_trunc(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_trunc_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_trunc_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn half_trunc_internal(&mut self, position: i16) { let digit_after_position = self.digit_at_next_position(position); let should_expand = match digit_after_position.cmp(&5) { Ordering::Less => false, @@ -1495,39 +1513,6 @@ impl FixedDecimal { } } - /// Half Truncates the number on the right to a particular position, deleting - /// digits if necessary. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("-1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("3.954").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("4", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_trunc(&mut self, position: i16) { - self.half_trunc_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Half Truncates the number on the right to a particular position and rounding increment, /// deleting digits if necessary. /// @@ -2114,8 +2099,20 @@ impl FixedDecimal { /// dec.half_expand(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_expand(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_expand_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_expand_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + pub fn half_expand_internal(&mut self, position: i16) { let digit_after_position = self.digit_at_next_position(position); if digit_after_position >= 5 { @@ -2125,35 +2122,6 @@ impl FixedDecimal { } } - /// Take the half expand of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_expand(&mut self, position: i16) { - self.half_expand_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half expand of the number at a particular position and rounding increment. /// ///
@@ -2300,8 +2268,20 @@ impl FixedDecimal { /// dec.ceil(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn ceil(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.ceil_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.ceil_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn ceil_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.trunc(position); return; @@ -2310,35 +2290,6 @@ impl FixedDecimal { self.expand(position); } - /// Take the ceiling of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.ceil(0); - /// assert_eq!("-1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.ceil(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn ceil(&mut self, position: i16) { - self.ceil_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the ceiling of the number at a particular position and rounding increment. /// ///
@@ -2478,8 +2429,20 @@ impl FixedDecimal { /// dec.half_ceil(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_ceil(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_ceil_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_ceil_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn half_ceil_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.half_trunc(position); return; @@ -2488,35 +2451,6 @@ impl FixedDecimal { self.half_expand(position); } - /// Take the half ceiling of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("-1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_ceil(&mut self, position: i16) { - self.half_ceil_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half ceiling of the number at a particular position and rounding increment. /// ///
@@ -2656,8 +2590,20 @@ impl FixedDecimal { /// dec.floor(0); /// assert_eq!("1", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn floor(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.floor_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.floor_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + pub fn floor_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.expand(position); return; @@ -2666,35 +2612,6 @@ impl FixedDecimal { self.trunc(position); } - /// Take the floor of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.floor(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.floor(0); - /// assert_eq!("1", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn floor(&mut self, position: i16) { - self.floor_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the floor of the number at a particular position and rounding increment. /// ///
@@ -2834,8 +2751,20 @@ impl FixedDecimal { /// dec.half_floor(0); /// assert_eq!("1", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_floor(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_floor_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_floor_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn half_floor_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.half_expand(position); return; @@ -2844,35 +2773,6 @@ impl FixedDecimal { self.half_trunc(position); } - /// Take the half floor of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("1", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_floor(&mut self, position: i16) { - self.half_floor_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half floor of the number at a particular position and rounding increment. /// ///
@@ -3016,8 +2916,20 @@ impl FixedDecimal { /// dec.half_even(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_even(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_even_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_even_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + pub fn half_even_internal(&mut self, position: i16) { let digit_after_position = self.digit_at_next_position(position); let should_expand = match digit_after_position.cmp(&5) { Ordering::Less => false, @@ -3039,35 +2951,6 @@ impl FixedDecimal { } } - /// Take the half even of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_even(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_even(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_even(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_even(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_even(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_even(&mut self, position: i16) { - self.half_even_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half even of the number at a particular position and rounding increment. /// ///
diff --git a/utils/fixed_decimal/tests/rounding.rs b/utils/fixed_decimal/tests/rounding.rs index a02bf5e2aba..6d403bdfa30 100644 --- a/utils/fixed_decimal/tests/rounding.rs +++ b/utils/fixed_decimal/tests/rounding.rs @@ -336,3 +336,90 @@ pub fn extra_rounding_mode_cases() { } } } + +#[test] +#[cfg(feature = "experimental")] +pub fn test_ecma402_table_with_increments() { + use fixed_decimal::RoundingIncrement; + + #[rustfmt::skip] // Don't split everything on its own line. Makes it look a lot nicer. + #[allow(clippy::type_complexity)] + let cases: [(_, _, [(_, fn(&mut FixedDecimal, i16, RoundingIncrement), _, _, _, _, _); 9]); 3] = [ + ("two", RoundingIncrement::MultiplesOf2, [ + ("ceil", FixedDecimal::ceil_to_increment, "-1.4", "0.4", "0.6", "0.6", "1.6"), + ("floor", FixedDecimal::floor_to_increment, "-1.6", "0.4", "0.4", "0.6", "1.4"), + ("expand", FixedDecimal::expand_to_increment, "-1.6", "0.4", "0.6", "0.6", "1.6"), + ("trunc", FixedDecimal::trunc_to_increment, "-1.4", "0.4", "0.4", "0.6", "1.4"), + ("half_ceil", FixedDecimal::half_ceil_to_increment, "-1.4", "0.4", "0.6", "0.6", "1.6"), + ("half_floor", FixedDecimal::half_floor_to_increment, "-1.6", "0.4", "0.4", "0.6", "1.4"), + ("half_expand", FixedDecimal::half_expand_to_increment, "-1.6", "0.4", "0.6", "0.6", "1.6"), + ("half_trunc", FixedDecimal::half_trunc_to_increment, "-1.4", "0.4", "0.4", "0.6", "1.4"), + ("half_even", FixedDecimal::half_even_to_increment, "-1.6", "0.4", "0.4", "0.6", "1.6"), + ]), + ("five", RoundingIncrement::MultiplesOf5, [ + ("ceil", FixedDecimal::ceil_to_increment, "-1.5", "0.5", "0.5", "1.0", "1.5"), + ("floor", FixedDecimal::floor_to_increment, "-1.5", "0.0", "0.5", "0.5", "1.5"), + ("expand", FixedDecimal::expand_to_increment, "-1.5", "0.5", "0.5", "1.0", "1.5"), + ("trunc", FixedDecimal::trunc_to_increment, "-1.5", "0.0", "0.5", "0.5", "1.5"), + ("half_ceil", FixedDecimal::half_ceil_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_floor", FixedDecimal::half_floor_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_expand", FixedDecimal::half_expand_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_trunc", FixedDecimal::half_trunc_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_even", FixedDecimal::half_even_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ]), + ("twenty-five", RoundingIncrement::MultiplesOf25, [ + ("ceil", FixedDecimal::ceil_to_increment, "-0.0", "2.5", "2.5", "2.5", "2.5"), + ("floor", FixedDecimal::floor_to_increment, "-2.5", "0.0", "0.0", "0.0", "0.0"), + ("expand", FixedDecimal::expand_to_increment, "-2.5", "2.5", "2.5", "2.5", "2.5"), + ("trunc", FixedDecimal::trunc_to_increment, "-0.0", "0.0", "0.0", "0.0", "0.0"), + ("half_ceil", FixedDecimal::half_ceil_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_floor", FixedDecimal::half_floor_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_expand", FixedDecimal::half_expand_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_trunc", FixedDecimal::half_trunc_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_even", FixedDecimal::half_even_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ]), + ]; + + for (increment_str, increment, cases) in cases { + for (rounding_mode, f, e1, e2, e3, e4, e5) in cases { + let mut fd1: FixedDecimal = "-1.5".parse().unwrap(); + let mut fd2: FixedDecimal = "0.4".parse().unwrap(); + let mut fd3: FixedDecimal = "0.5".parse().unwrap(); + let mut fd4: FixedDecimal = "0.6".parse().unwrap(); + let mut fd5: FixedDecimal = "1.5".parse().unwrap(); + // The original ECMA-402 table tests rounding at magnitude 0. + // However, testing rounding at magnitude -1 gives more + // interesting test cases for increments. + f(&mut fd1, -1, increment); + f(&mut fd2, -1, increment); + f(&mut fd3, -1, increment); + f(&mut fd4, -1, increment); + f(&mut fd5, -1, increment); + assert_eq!( + fd1.write_to_string(), + e1, + "-1.5 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd2.write_to_string(), + e2, + "0.4 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd3.write_to_string(), + e3, + "0.5 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd4.write_to_string(), + e4, + "0.6 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd5.write_to_string(), + e5, + "1.5 failed for {rounding_mode} with increments of {increment_str}" + ); + } + } +} From 72e33941955eccb189f26fba4249eded86fc2eb6 Mon Sep 17 00:00:00 2001 From: Robert Bastian <4706271+robertbastian@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:10:43 +0100 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5afb47d7e64..56091b12084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ - Add implementations for `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap` (https://github.com/unicode-org/icu4x/pull/4268, https://github.com/unicode-org/icu4x/pull/4274, https://github.com/unicode-org/icu4x/pull/4295) - Improvements to `databake::test_bake!()` (https://github.com/unicode-org/icu4x/pull/4182) - `fixed_decimal` - - Experimental rounding increment support (https://github.com/unicode-org/icu4x/pull/4219) + - Experimental rounding increment support (https://github.com/unicode-org/icu4x/pull/4219, https://github.com/unicode-org/icu4x/pull/4246) - `litemap` - Implement `databake::Bake` on `LiteMap` (https://github.com/unicode-org/icu4x/pull/4275) - `tinystr`