Skip to content

Commit 707fab4

Browse files
committed
Move conversion code to minifloat!
1 parent a6b1e62 commit 707fab4

File tree

3 files changed

+123
-83
lines changed

3 files changed

+123
-83
lines changed

src/lib.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,129 @@ macro_rules! minifloat {
441441
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
442442
Self::from_bits(magnitude.min(i64::from(Self::HUGE.to_bits())) as $bits | sign_bit)
443443
}
444+
445+
/// Fast conversion to [`f32`]
446+
///
447+
/// This method serves as a shortcut if conversion to [`f32`] is
448+
/// lossless.
449+
fn fast_to_f32(self) -> f32 {
450+
let sign = if self.is_sign_negative() { -1.0 } else { 1.0 };
451+
let magnitude = self.to_bits() & Self::ABS_MASK;
452+
453+
if self.is_nan() {
454+
return f32::NAN.copysign(sign);
455+
}
456+
if self.is_infinite() {
457+
return f32::INFINITY * sign;
458+
}
459+
if magnitude < 1 << Self::M {
460+
#[allow(clippy::cast_possible_wrap)]
461+
let shift = Self::MIN_EXP - Self::MANTISSA_DIGITS as i32;
462+
#[allow(clippy::cast_possible_truncation)]
463+
return ($crate::detail::exp2i(shift) * f64::from(sign) * f64::from(magnitude)) as f32;
464+
}
465+
let shift = f32::MANTISSA_DIGITS - Self::MANTISSA_DIGITS;
466+
#[allow(clippy::cast_sign_loss)]
467+
let diff = (Self::MIN_EXP - f32::MIN_EXP) as u32;
468+
let diff = diff << (f32::MANTISSA_DIGITS - 1);
469+
let sign = u32::from(self.is_sign_negative()) << 31;
470+
f32::from_bits(((u32::from(magnitude) << shift) + diff) | sign)
471+
}
472+
473+
/// Fast conversion to [`f64`]
474+
///
475+
/// This method serves as a shortcut if conversion to [`f64`] is
476+
/// lossless.
477+
fn fast_to_f64(self) -> f64 {
478+
let sign = if self.is_sign_negative() { -1.0 } else { 1.0 };
479+
let magnitude = self.to_bits() & Self::ABS_MASK;
480+
481+
if self.is_nan() {
482+
return f64::NAN.copysign(sign);
483+
}
484+
if self.is_infinite() {
485+
return f64::INFINITY * sign;
486+
}
487+
if magnitude < 1 << Self::M {
488+
#[allow(clippy::cast_possible_wrap)]
489+
let shift = Self::MIN_EXP - Self::MANTISSA_DIGITS as i32;
490+
return $crate::detail::exp2i(shift) * sign * f64::from(magnitude);
491+
}
492+
let shift = f64::MANTISSA_DIGITS - Self::MANTISSA_DIGITS;
493+
#[allow(clippy::cast_sign_loss)]
494+
let diff = (Self::MIN_EXP - f64::MIN_EXP) as u64;
495+
let diff = diff << (f64::MANTISSA_DIGITS - 1);
496+
let sign = u64::from(self.is_sign_negative()) << 63;
497+
f64::from_bits(((u64::from(magnitude) << shift) + diff) | sign)
498+
}
499+
500+
/// Lossy conversion to [`f64`]
501+
///
502+
/// This variant assumes that the conversion is lossy only when the exponent
503+
/// is out of range.
504+
fn as_f64(self) -> f64 {
505+
let bias = (1 << (Self::E - 1)) - 1;
506+
let sign = if self.is_sign_negative() { -1.0 } else { 1.0 };
507+
let magnitude = self.abs().to_bits();
508+
509+
if self.is_nan() {
510+
return f64::NAN.copysign(sign);
511+
}
512+
if self.is_infinite() {
513+
return f64::INFINITY * sign;
514+
}
515+
if i32::from(magnitude) >= (f64::MAX_EXP + bias) << Self::M {
516+
return f64::INFINITY * sign;
517+
}
518+
if magnitude < 1 << Self::M {
519+
#[allow(clippy::cast_possible_wrap)]
520+
let shift = Self::MIN_EXP - Self::MANTISSA_DIGITS as i32;
521+
return $crate::detail::exp2i(shift) * sign * f64::from(magnitude);
522+
}
523+
if i32::from(magnitude >> Self::M) < f64::MIN_EXP + bias {
524+
let significand = (magnitude & ((1 << Self::M) - 1)) | 1 << Self::M;
525+
let exponent = i32::from(magnitude >> Self::M) - bias;
526+
#[allow(clippy::cast_possible_wrap)]
527+
return $crate::detail::exp2i(exponent - Self::M as i32) * sign * f64::from(significand);
528+
}
529+
let shift = f64::MANTISSA_DIGITS - Self::MANTISSA_DIGITS;
530+
#[allow(clippy::cast_sign_loss)]
531+
let diff = (Self::MIN_EXP - f64::MIN_EXP) as u64;
532+
let diff = diff << (f64::MANTISSA_DIGITS - 1);
533+
let sign = u64::from(self.is_sign_negative()) << 63;
534+
f64::from_bits(((u64::from(magnitude) << shift) + diff) | sign)
535+
}
536+
537+
/// Best effort conversion to [`f64`]
538+
#[must_use]
539+
pub fn to_f64(self) -> f64 {
540+
let lossless = f64::MANTISSA_DIGITS >= Self::MANTISSA_DIGITS
541+
&& f64::MAX_EXP >= Self::MAX_EXP
542+
&& f64::MIN_EXP <= Self::MIN_EXP;
543+
544+
if lossless {
545+
self.fast_to_f64()
546+
} else {
547+
self.as_f64()
548+
}
549+
}
550+
551+
/// Best effort conversion to [`f32`]
552+
#[must_use]
553+
pub fn to_f32(self) -> f32 {
554+
let lossless = f32::MANTISSA_DIGITS >= Self::MANTISSA_DIGITS
555+
&& f32::MAX_EXP >= Self::MAX_EXP
556+
&& f32::MIN_EXP <= Self::MIN_EXP;
557+
558+
if lossless {
559+
return self.fast_to_f32();
560+
}
561+
// Conversion to `f64` is lossy only when then exponent width is
562+
// too large. In this case, a second conversion to `f32` is
563+
// safe.
564+
#[allow(clippy::cast_possible_truncation)]
565+
return self.to_f64() as f32;
566+
}
444567
}
445568

446569
const _: () = assert!($name::BITWIDTH <= 16);

src/most16.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -228,37 +228,3 @@ pub trait Most16<const M: u32>: Sized + Copy + PartialEq + PartialOrd + Neg<Outp
228228
self.to_bits() >> (Self::E + Self::M) & 1 == 1
229229
}
230230
}
231-
232-
/// Lossy conversion to [`f64`]
233-
fn as_f64<const M: u32, T: Most16<M>>(x: T) -> f64 {
234-
let bias = (1 << (T::E - 1)) - 1;
235-
let sign = if x.is_sign_negative() { -1.0 } else { 1.0 };
236-
let magnitude = x.abs().to_bits();
237-
238-
if x.is_nan() {
239-
return f64::NAN.copysign(sign);
240-
}
241-
if x.is_infinite() {
242-
return f64::INFINITY * sign;
243-
}
244-
if i32::from(magnitude) >= (f64::MAX_EXP + bias) << M {
245-
return f64::INFINITY * sign;
246-
}
247-
if magnitude < 1 << M {
248-
#[allow(clippy::cast_possible_wrap)]
249-
let shift = T::MIN_EXP - T::MANTISSA_DIGITS as i32;
250-
return crate::detail::exp2i(shift) * sign * f64::from(magnitude);
251-
}
252-
if i32::from(magnitude >> M) < f64::MIN_EXP + bias {
253-
let significand = (magnitude & ((1 << M) - 1)) | 1 << M;
254-
let exponent = i32::from(magnitude >> M) - bias;
255-
#[allow(clippy::cast_possible_wrap)]
256-
return crate::detail::exp2i(exponent - M as i32) * sign * f64::from(significand);
257-
}
258-
let shift = f64::MANTISSA_DIGITS - T::MANTISSA_DIGITS;
259-
#[allow(clippy::cast_sign_loss)]
260-
let diff = (T::MIN_EXP - f64::MIN_EXP) as u64;
261-
let diff = diff << (f64::MANTISSA_DIGITS - 1);
262-
let sign = u64::from(x.is_sign_negative()) << 63;
263-
f64::from_bits(((u64::from(magnitude) << shift) + diff) | sign)
264-
}

src/most8.rs

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -226,53 +226,4 @@ pub trait Most8<const M: u32>: Sized + Copy + PartialEq + PartialOrd + Neg<Outpu
226226
fn is_sign_negative(self) -> bool {
227227
self.to_bits() >> (Self::E + Self::M) & 1 == 1
228228
}
229-
230-
/// Lossless conversion to [`f32`]
231-
fn to_f32(self) -> f32 {
232-
let sign = if self.is_sign_negative() { -1.0 } else { 1.0 };
233-
let magnitude = self.to_bits() & Self::ABS_MASK;
234-
235-
if self.is_nan() {
236-
return f32::NAN.copysign(sign);
237-
}
238-
if self.is_infinite() {
239-
return f32::INFINITY * sign;
240-
}
241-
if magnitude < 1 << Self::M {
242-
#[allow(clippy::cast_possible_wrap)]
243-
let shift = Self::MIN_EXP - Self::MANTISSA_DIGITS as i32;
244-
#[allow(clippy::cast_possible_truncation)]
245-
return (crate::detail::exp2i(shift) * f64::from(sign) * f64::from(magnitude)) as f32;
246-
}
247-
let shift = f32::MANTISSA_DIGITS - Self::MANTISSA_DIGITS;
248-
#[allow(clippy::cast_sign_loss)]
249-
let diff = (Self::MIN_EXP - f32::MIN_EXP) as u32;
250-
let diff = diff << (f32::MANTISSA_DIGITS - 1);
251-
let sign = u32::from(self.is_sign_negative()) << 31;
252-
f32::from_bits(((u32::from(magnitude) << shift) + diff) | sign)
253-
}
254-
255-
/// Lossless conversion to [`f64`]
256-
fn to_f64(self) -> f64 {
257-
let sign = if self.is_sign_negative() { -1.0 } else { 1.0 };
258-
let magnitude = self.to_bits() & Self::ABS_MASK;
259-
260-
if self.is_nan() {
261-
return f64::NAN.copysign(sign);
262-
}
263-
if self.is_infinite() {
264-
return f64::INFINITY * sign;
265-
}
266-
if magnitude < 1 << M {
267-
#[allow(clippy::cast_possible_wrap)]
268-
let shift = Self::MIN_EXP - Self::MANTISSA_DIGITS as i32;
269-
return crate::detail::exp2i(shift) * sign * f64::from(magnitude);
270-
}
271-
let shift = f64::MANTISSA_DIGITS - Self::MANTISSA_DIGITS;
272-
#[allow(clippy::cast_sign_loss)]
273-
let diff = (Self::MIN_EXP - f64::MIN_EXP) as u64;
274-
let diff = diff << (f64::MANTISSA_DIGITS - 1);
275-
let sign = u64::from(self.is_sign_negative()) << 63;
276-
f64::from_bits(((u64::from(magnitude) << shift) + diff) | sign)
277-
}
278229
}

0 commit comments

Comments
 (0)