From 8fad4393fec11d2717eeb262d0a3e247adf36e01 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 24 Aug 2025 15:17:47 -0700 Subject: [PATCH] rust: Make .display_plain() for f64 support alternate decimal points. --- rust/pspp/src/format/display/mod.rs | 62 ++++++++++++++++++++++------- rust/pspp/src/sys/raw/records.rs | 6 +-- rust/pspp/src/sys/write.rs | 4 +- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/rust/pspp/src/format/display/mod.rs b/rust/pspp/src/format/display/mod.rs index 6f65cee10e..79a4c106f1 100644 --- a/rust/pspp/src/format/display/mod.rs +++ b/rust/pspp/src/format/display/mod.rs @@ -56,29 +56,63 @@ pub struct DisplayDatum<'b, B> { mod test; pub trait DisplayPlain { - fn display_plain(&self) -> impl Display; + fn display_plain(&self) -> DisplayPlainF64; } impl DisplayPlain for f64 { - fn display_plain(&self) -> impl Display { - DisplayPlainF64(*self) + fn display_plain(&self) -> DisplayPlainF64 { + DisplayPlainF64 { + value: *self, + decimal: '.', + } } } -pub struct DisplayPlainF64(pub f64); +pub struct DisplayPlainF64 { + pub value: f64, + pub decimal: char, +} + +impl DisplayPlainF64 { + pub fn with_decimal(self, decimal: char) -> Self { + Self { decimal, ..self } + } +} impl Display for DisplayPlainF64 { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if self.0.abs() < 0.0005 || self.0.abs() > 1e15 { - // Print self.0s that would otherwise have lots of leading or - // trailing zeros in scientific notation with full precision. - write!(f, "{:.e}", self.0) - } else if self.0 == self.0.trunc() { - // Print integers without decimal places. - write!(f, "{:.0}", self.0) - } else { - // Print other numbers with full precision. - write!(f, "{:.}", self.0) + struct Inner(f64); + + impl Display for Inner { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let value = self.0; + if (value.abs() < 0.0005 && value != 0.0) || value.abs() > 1e15 { + // Print 0s that would otherwise have lots of leading or + // trailing zeros in scientific notation with full precision. + write!(f, "{value:.e}") + } else if value == value.trunc() { + // Print integers without decimal places. + write!(f, "{value:.0}") + } else { + // Print other numbers with full precision. + write!(f, "{value:.}") + } + } + } + + match self.decimal { + '.' => write!(f, "{}", Inner(self.value)), + _ => { + let mut tmp = SmallString::<[u8; 64]>::new(); + write!(&mut tmp, "{}", Inner(self.value)).unwrap(); + if let Some(position) = tmp.find('.') { + f.write_str(&tmp[..position])?; + f.write_char(self.decimal)?; + f.write_str(&tmp[position + 1..]) + } else { + f.write_str(&tmp) + } + } } } } diff --git a/rust/pspp/src/sys/raw/records.rs b/rust/pspp/src/sys/raw/records.rs index 5269d26d98..b54f8d6da3 100644 --- a/rust/pspp/src/sys/raw/records.rs +++ b/rust/pspp/src/sys/raw/records.rs @@ -15,7 +15,7 @@ use crate::{ data::{ByteStrArray, ByteString, Datum}, dictionary::CategoryLabels, endian::FromBytes, - format::{DisplayPlainF64, Format, Type}, + format::{DisplayPlain, Format, Type}, identifier::{Error as IdError, Identifier}, sys::{ raw::{ @@ -2672,8 +2672,8 @@ pub enum ZTrailerError { /// ZLIB trailer bias {actual} is not {} as expected from file header bias. #[ error( - "Bias {actual} is not {} as expected from file header.", - DisplayPlainF64(*expected) + "Bias {actual} is not {} as expected from file header.", + expected.display_plain() )] WrongZlibTrailerBias { /// ZLIB trailer bias read from file. diff --git a/rust/pspp/src/sys/write.rs b/rust/pspp/src/sys/write.rs index 8b12ea268e..d67b73575b 100644 --- a/rust/pspp/src/sys/write.rs +++ b/rust/pspp/src/sys/write.rs @@ -35,7 +35,7 @@ use smallvec::SmallVec; use crate::{ data::{Datum, RawString}, dictionary::{CategoryLabels, Dictionary, MultipleResponseType}, - format::{DisplayPlainF64, Format}, + format::{DisplayPlain, Format}, identifier::Identifier, output::spv::Zeros, sys::{ @@ -536,7 +536,7 @@ where let mut value = match datum { Datum::Number(Some(number)) => { - DisplayPlainF64(*number).to_string().into_bytes() + number.display_plain().to_string().into_bytes() } Datum::Number(None) => vec![b'.'], Datum::String(raw_string) => raw_string.0.clone(), -- 2.30.2