Skip to content

Commit

Permalink
Improvements to FixedDecimal f64 APIs (#1718)
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc authored Mar 24, 2022
1 parent a4c650b commit d9756b8
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 19 deletions.
6 changes: 3 additions & 3 deletions ffi/diplomat/src/fixed_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub mod ffi {
/// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.from_f64) for more information.
pub fn create_from_f64_with_max_precision(f: f64) -> Option<Box<ICU4XFixedDecimal>> {
Some(Box::new(ICU4XFixedDecimal(
FixedDecimal::new_from_f64(f, DoublePrecision::Maximum).ok()?,
FixedDecimal::try_from_f64(f, DoublePrecision::Floating).ok()?,
)))
}

Expand All @@ -57,7 +57,7 @@ pub mod ffi {
rounding_mode: ICU4XFixedDecimalRoundingMode,
) -> Option<Box<ICU4XFixedDecimal>> {
Some(Box::new(ICU4XFixedDecimal(
FixedDecimal::new_from_f64(
FixedDecimal::try_from_f64(
f,
DoublePrecision::Magnitude(precision, rounding_mode.into()),
)
Expand All @@ -74,7 +74,7 @@ pub mod ffi {
rounding_mode: ICU4XFixedDecimalRoundingMode,
) -> Option<Box<ICU4XFixedDecimal>> {
Some(Box::new(ICU4XFixedDecimal(
FixedDecimal::new_from_f64(
FixedDecimal::try_from_f64(
f,
DoublePrecision::SignificantDigits(digits, rounding_mode.into()),
)
Expand Down
50 changes: 34 additions & 16 deletions utils/fixed_decimal/src/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ pub enum DoublePrecision {
///
/// This results in a FixedDecimal having enough digits to recover the original floating point
/// value, with no trailing zeros.
Maximum,
Floating,
}

/// Specifies how numbers should be rounded
Expand All @@ -901,27 +901,44 @@ pub enum RoundingMode {

#[cfg(feature = "ryu")]
impl FixedDecimal {
/// Construct a [`FixedDecimal`] from an f64. This uses `ryu` and
/// goes through an intermediate string representation, so is not
/// fully efficient. See [icu4x#166](https://github.com/unicode-org/icu4x/issues/166) for
/// more details.
/// Construct a [`FixedDecimal`] from an f64.
///
/// Since f64 values do not carry a notion of their precision, the second argument to this
/// function specifies the type of precision associated with the f64. For more information,
/// see [`DoublePrecision`].
///
/// This function uses `ryu`, which is an efficient double-to-string algorithm, but other
/// implementations may yield higher performance; for more details, see
/// [icu4x#166](https://github.com/unicode-org/icu4x/issues/166).
///
/// This function can be made available with the `"ryu"` feature.
///
/// ```rust
/// use fixed_decimal::{DoublePrecision, FixedDecimal};
/// use fixed_decimal::{DoublePrecision, FixedDecimal, RoundingMode};
/// use writeable::Writeable;
///
/// let decimal = FixedDecimal::new_from_f64(0.012345678, DoublePrecision::Maximum).unwrap();
/// let decimal = FixedDecimal::try_from_f64(
/// -5.1,
/// DoublePrecision::Magnitude(-2, RoundingMode::Unnecessary)
/// )
/// .expect("Finite quantity with limited precision");
/// assert_eq!(decimal.write_to_string(), "-5.10");
///
/// let decimal = FixedDecimal::try_from_f64(
/// 0.012345678,
/// DoublePrecision::Floating
/// )
/// .expect("Finite quantity");
/// assert_eq!(decimal.write_to_string(), "0.012345678");
///
/// let decimal = FixedDecimal::new_from_f64(-123456.78, DoublePrecision::Maximum).unwrap();
/// assert_eq!(decimal.write_to_string(), "-123456.78");
///
/// let decimal = FixedDecimal::new_from_f64(12345678000., DoublePrecision::Maximum).unwrap();
/// let decimal = FixedDecimal::try_from_f64(
/// 12345678000.,
/// DoublePrecision::Integer
/// )
/// .expect("Finite, integer-valued quantity");
/// assert_eq!(decimal.write_to_string(), "12345678000");
/// ```
pub fn new_from_f64(float: f64, precision: DoublePrecision) -> Result<Self, Error> {
pub fn try_from_f64(float: f64, precision: DoublePrecision) -> Result<Self, Error> {
let mut decimal = Self::new_from_f64_raw(float)?;
let n_digits = decimal.digits.len();
// magnitude of the lowest digit in self.digits
Expand All @@ -932,7 +949,7 @@ impl FixedDecimal {
decimal.lower_magnitude = 0;
}
match precision {
DoublePrecision::Maximum => (),
DoublePrecision::Floating => (),
DoublePrecision::Integer => {
if lowest_magnitude < 0 {
return Err(Error::Limit);
Expand Down Expand Up @@ -989,6 +1006,7 @@ impl FixedDecimal {
decimal.check_invariants();
Ok(decimal)
}

/// Internal function for parsing directly from floats using ryū
fn new_from_f64_raw(float: f64) -> Result<Self, Error> {
if !float.is_finite() {
Expand Down Expand Up @@ -1165,12 +1183,12 @@ fn test_float() {
let cases = [
TestCase {
input: 1.234567,
precision: DoublePrecision::Maximum,
precision: DoublePrecision::Floating,
expected: "1.234567",
},
TestCase {
input: 888999.,
precision: DoublePrecision::Maximum,
precision: DoublePrecision::Floating,
expected: "888999",
},
// HalfExpand tests
Expand Down Expand Up @@ -1338,7 +1356,7 @@ fn test_float() {
];

for case in &cases {
let dec = FixedDecimal::new_from_f64(case.input, case.precision).unwrap();
let dec = FixedDecimal::try_from_f64(case.input, case.precision).unwrap();
writeable::assert_writeable_eq!(dec, case.expected, "{:?}", case);
}
}
Expand Down
1 change: 1 addition & 0 deletions utils/fixed_decimal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod uint_iterator;
pub use decimal::DoublePrecision;

pub use decimal::FixedDecimal;
pub use decimal::RoundingMode;
use displaydoc::Display;
pub use signum::Signum;

Expand Down

0 comments on commit d9756b8

Please sign in to comment.