Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix serializing -0.0 floats. #116

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 304 additions & 0 deletions lexical-write-float/file.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
diff --git a/lexical-write-float/src/algorithm.rs b/lexical-write-float/src/algorithm.rs
index a16dcdb..de7ec62 100644
--- a/lexical-write-float/src/algorithm.rs
+++ b/lexical-write-float/src/algorithm.rs
@@ -64,6 +64,7 @@ pub unsafe fn write_float<F: RawFloat, const FORMAT: u128>(
// in most cases.

write_float!(
+ float,
FORMAT,
sci_exp,
options,
@@ -71,7 +72,8 @@ pub unsafe fn write_float<F: RawFloat, const FORMAT: u128>(
write_float_positive_exponent,
write_float_negative_exponent,
generic => F,
- args => bytes, fp, sci_exp, options,
+ bytes => bytes,
+ args => fp, sci_exp, options,
)
}

@@ -153,7 +155,7 @@ pub unsafe fn write_float_negative_exponent<F: DragonboxFloat, const FORMAT: u12
sci_exp: i32,
options: &Options,
) -> usize {
- debug_assert!(sci_exp < 0);
+ debug_assert!(sci_exp < 0 || fp.mant == 0);
debug_assert_eq!(count_factors(10, fp.mant), 0);

// Config options.
diff --git a/lexical-write-float/src/binary.rs b/lexical-write-float/src/binary.rs
index de8ab8e..13dbc97 100644
--- a/lexical-write-float/src/binary.rs
+++ b/lexical-write-float/src/binary.rs
@@ -85,6 +85,7 @@ where
}

write_float!(
+ float,
FORMAT,
sci_exp,
options,
@@ -92,7 +93,8 @@ where
write_float_positive_exponent,
write_float_negative_exponent,
generic => _,
- args => mantissa, exp, sci_exp, bytes, options,
+ bytes => bytes,
+ args => mantissa, exp, sci_exp, options,
)
}

@@ -110,10 +112,10 @@ where
/// and `mantissa_radix` in `FORMAT` must be identical.
#[inline(always)]
pub unsafe fn write_float_scientific<M, const FORMAT: u128>(
+ bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
- bytes: &mut [u8],
options: &Options,
) -> usize
where
@@ -191,10 +193,10 @@ where
/// significant digits and the leading zeros.
#[inline(always)]
pub unsafe fn write_float_negative_exponent<M, const FORMAT: u128>(
+ bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
- bytes: &mut [u8],
options: &Options,
) -> usize
where
@@ -275,10 +277,10 @@ where
/// significant digits and the (optional) trailing zeros.
#[inline(always)]
pub unsafe fn write_float_positive_exponent<M, const FORMAT: u128>(
+ bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
- bytes: &mut [u8],
options: &Options,
) -> usize
where
diff --git a/lexical-write-float/src/compact.rs b/lexical-write-float/src/compact.rs
index 19960b7..b3e99d1 100644
--- a/lexical-write-float/src/compact.rs
+++ b/lexical-write-float/src/compact.rs
@@ -83,13 +83,15 @@ pub unsafe fn write_float<F: RawFloat, const FORMAT: u128>(

let sci_exp = kappa + digit_count as i32 - 1 + carried as i32;
write_float!(
+ float,
FORMAT,
sci_exp,
options,
write_float_scientific,
write_float_positive_exponent,
write_float_negative_exponent,
- args => bytes, &mut digits, digit_count, sci_exp, options,
+ bytes => bytes,
+ args => &mut digits, digit_count, sci_exp, options,
)
}

diff --git a/lexical-write-float/src/hex.rs b/lexical-write-float/src/hex.rs
index a3d7c71..5124525 100644
--- a/lexical-write-float/src/hex.rs
+++ b/lexical-write-float/src/hex.rs
@@ -107,6 +107,7 @@ where
}

write_float!(
+ float,
FORMAT,
sci_exp,
options,
@@ -114,7 +115,8 @@ where
write_float_positive_exponent,
write_float_negative_exponent,
generic => _,
- args => mantissa, exp, sci_exp, bytes, options,
+ bytes => bytes,
+ args => mantissa, exp, sci_exp, options,
)
}

@@ -131,10 +133,10 @@ where
/// based on the number of maximum digits.
#[inline(always)]
pub unsafe fn write_float_scientific<M, const FORMAT: u128>(
+ bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
- bytes: &mut [u8],
options: &Options,
) -> usize
where
diff --git a/lexical-write-float/src/radix.rs b/lexical-write-float/src/radix.rs
index dda56ab..dc9cc7d 100644
--- a/lexical-write-float/src/radix.rs
+++ b/lexical-write-float/src/radix.rs
@@ -178,13 +178,15 @@ where
let zero_count = ltrim_char_count(digits, b'0');
let sci_exp: i32 = initial_cursor as i32 - integer_cursor as i32 - zero_count as i32 - 1;
write_float!(
+ float,
FORMAT,
sci_exp,
options,
write_float_scientific,
write_float_nonscientific,
write_float_nonscientific,
- args => sci_exp, &mut buffer, bytes, initial_cursor,
+ bytes => bytes,
+ args => sci_exp, &mut buffer, initial_cursor,
integer_cursor, fraction_cursor, options,
)
}
@@ -203,9 +205,9 @@ where
/// and `mantissa_radix` in `FORMAT` must be identical.
#[inline(always)]
pub unsafe fn write_float_scientific<const FORMAT: u128>(
+ bytes: &mut [u8],
sci_exp: i32,
buffer: &mut [u8],
- bytes: &mut [u8],
initial_cursor: usize,
integer_cursor: usize,
fraction_cursor: usize,
@@ -294,9 +296,9 @@ pub unsafe fn write_float_scientific<const FORMAT: u128>(
/// significant digits and the leading zeros.
#[inline(always)]
pub unsafe fn write_float_nonscientific<const FORMAT: u128>(
+ bytes: &mut [u8],
_: i32,
buffer: &mut [u8],
- bytes: &mut [u8],
initial_cursor: usize,
integer_cursor: usize,
fraction_cursor: usize,
diff --git a/lexical-write-float/src/shared.rs b/lexical-write-float/src/shared.rs
index f6b838a..c4d4369 100644
--- a/lexical-write-float/src/shared.rs
+++ b/lexical-write-float/src/shared.rs
@@ -171,6 +171,7 @@ pub unsafe fn write_exponent<const FORMAT: u128>(
/// Detect the notation to use for the float formatter and call the appropriate function..
macro_rules! write_float {
(
+ $float:ident,
$format:ident,
$sci_exp:ident,
$options:ident,
@@ -178,6 +179,7 @@ macro_rules! write_float {
$write_positive:ident,
$write_negative:ident,
$(generic => $generic:tt,)?
+ bytes => $bytes:ident,
args => $($args:expr,)*
) => {{
use lexical_util::format::NumberFormat;
@@ -191,15 +193,22 @@ macro_rules! write_float {
if !format.no_exponent_notation() && require_exponent {
// Write digits in scientific notation.
// SAFETY: safe as long as bytes is large enough to hold all the digits.
- unsafe { $write_scientific::<$($generic,)? FORMAT>($($args,)*) }
- } else if $sci_exp >= 0 {
- // Write positive exponent without scientific notation.
+ unsafe { $write_scientific::<$($generic,)? FORMAT>($bytes, $($args,)*) }
+ } else if $sci_exp < 0 {
+ // Write negative exponent without scientific notation.
// SAFETY: safe as long as bytes is large enough to hold all the digits.
- unsafe { $write_positive::<$($generic,)? FORMAT>($($args,)*) }
+ unsafe { $write_negative::<$($generic,)? FORMAT>($bytes, $($args,)*) }
+ } else if $float.is_sign_negative() {
+ // handle this as a positive, just write a leading '-' and then add 1 to our count
+ // # Safety: This is always safe since our buffer is much larger than 1 byte.
+ unsafe { index_unchecked_mut!($bytes[0]) = b'-'; }
+ // # Safety: This is always safe since our buffer is much larger than 1 byte.
+ let bytes = unsafe { &mut index_unchecked_mut!($bytes[1..]) };
+ unsafe { $write_positive::<$($generic,)? FORMAT>(bytes, $($args,)*) + 1 }
} else {
- // Write negative exponent without scientific notation.
+ // Write positive exponent without scientific notation.
// SAFETY: safe as long as bytes is large enough to hold all the digits.
- unsafe { $write_negative::<$($generic,)? FORMAT>($($args,)*) }
+ unsafe { $write_positive::<$($generic,)? FORMAT>($bytes, $($args,)*) }
}
}};
}
diff --git a/lexical-write-float/tests/binary_tests.rs b/lexical-write-float/tests/binary_tests.rs
index ac27db9..e488182 100644
--- a/lexical-write-float/tests/binary_tests.rs
+++ b/lexical-write-float/tests/binary_tests.rs
@@ -177,7 +177,7 @@ where
}

let count = unsafe {
- binary::write_float_scientific::<_, FORMAT>(mantissa, exp, sci_exp, &mut buffer, options)
+ binary::write_float_scientific::<_, FORMAT>(&mut buffer, mantissa, exp, sci_exp, options)
};
let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) };
assert_eq!(actual, expected);
@@ -465,10 +465,10 @@ fn write_float_negative_exponent<T: Float, const FORMAT: u128>(

let count = unsafe {
binary::write_float_negative_exponent::<_, FORMAT>(
+ &mut buffer,
mantissa,
exp,
sci_exp,
- &mut buffer,
options,
)
};
@@ -708,10 +708,10 @@ fn write_float_positive_exponent<T: Float, const FORMAT: u128>(

let count = unsafe {
binary::write_float_positive_exponent::<_, FORMAT>(
+ &mut buffer,
mantissa,
exp,
sci_exp,
- &mut buffer,
options,
)
};
diff --git a/lexical-write-float/tests/hex_tests.rs b/lexical-write-float/tests/hex_tests.rs
index ac8ff68..a4827c2 100644
--- a/lexical-write-float/tests/hex_tests.rs
+++ b/lexical-write-float/tests/hex_tests.rs
@@ -50,7 +50,7 @@ where
}

let count = unsafe {
- hex::write_float_scientific::<_, FORMAT>(mantissa, exp, sci_exp, &mut buffer, options)
+ hex::write_float_scientific::<_, FORMAT>(&mut buffer, mantissa, exp, sci_exp, options)
};
let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) };
assert_eq!(actual, expected);
diff --git a/lexical-write-float/tests/issue_94_tests.rs b/lexical-write-float/tests/issue_94_tests.rs
new file mode 100644
index 0000000..13b2dbf
--- /dev/null
+++ b/lexical-write-float/tests/issue_94_tests.rs
@@ -0,0 +1,12 @@
+use core::str;
+
+use lexical_util::constants::BUFFER_SIZE;
+use lexical_write_float::ToLexical;
+
+#[test]
+fn issue_94_test() {
+ let mut buffer = [b'\x00'; BUFFER_SIZE];
+ let neg0: f64 = -0.0;
+ let result = neg0.to_lexical(&mut buffer);
+ assert_eq!(str::from_utf8(result), Ok("-0.0"));
+}
4 changes: 3 additions & 1 deletion lexical-write-float/src/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,16 @@ pub unsafe fn write_float<F: RawFloat, const FORMAT: u128>(
// in most cases.

write_float!(
float,
FORMAT,
sci_exp,
options,
write_float_scientific,
write_float_positive_exponent,
write_float_negative_exponent,
generic => F,
args => bytes, fp, sci_exp, options,
bytes => bytes,
args => fp, sci_exp, options,
)
}

Expand Down
10 changes: 6 additions & 4 deletions lexical-write-float/src/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,16 @@ where
}

write_float!(
float,
FORMAT,
sci_exp,
options,
write_float_scientific,
write_float_positive_exponent,
write_float_negative_exponent,
generic => _,
args => mantissa, exp, sci_exp, bytes, options,
bytes => bytes,
args => mantissa, exp, sci_exp, options,
)
}

Expand All @@ -110,10 +112,10 @@ where
/// and `mantissa_radix` in `FORMAT` must be identical.
#[inline(always)]
pub unsafe fn write_float_scientific<M, const FORMAT: u128>(
bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
bytes: &mut [u8],
options: &Options,
) -> usize
where
Expand Down Expand Up @@ -191,10 +193,10 @@ where
/// significant digits and the leading zeros.
#[inline(always)]
pub unsafe fn write_float_negative_exponent<M, const FORMAT: u128>(
bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
bytes: &mut [u8],
options: &Options,
) -> usize
where
Expand Down Expand Up @@ -275,10 +277,10 @@ where
/// significant digits and the (optional) trailing zeros.
#[inline(always)]
pub unsafe fn write_float_positive_exponent<M, const FORMAT: u128>(
bytes: &mut [u8],
mantissa: M,
exp: i32,
sci_exp: i32,
bytes: &mut [u8],
options: &Options,
) -> usize
where
Expand Down
4 changes: 3 additions & 1 deletion lexical-write-float/src/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ pub unsafe fn write_float<F: RawFloat, const FORMAT: u128>(

let sci_exp = kappa + digit_count as i32 - 1 + carried as i32;
write_float!(
float,
FORMAT,
sci_exp,
options,
write_float_scientific,
write_float_positive_exponent,
write_float_negative_exponent,
args => bytes, &mut digits, digit_count, sci_exp, options,
bytes => bytes,
args => &mut digits, digit_count, sci_exp, options,
)
}

Expand Down
Loading