Skip to content

Commit

Permalink
Merge pull request #1005 from dtolnay/f32cast
Browse files Browse the repository at this point in the history
Eliminate f32-to-f64 casting in arbitrary_precision mode
  • Loading branch information
dtolnay committed Mar 27, 2023
2 parents 02e5833 + c9bff92 commit 731886c
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 7 deletions.
29 changes: 29 additions & 0 deletions src/number.rs
Expand Up @@ -279,6 +279,35 @@ impl Number {
}
}

pub(crate) fn as_f32(&self) -> Option<f32> {
#[cfg(not(feature = "arbitrary_precision"))]
match self.n {
N::PosInt(n) => Some(n as f32),
N::NegInt(n) => Some(n as f32),
N::Float(n) => Some(n as f32),
}
#[cfg(feature = "arbitrary_precision")]
self.n.parse::<f32>().ok().filter(|float| float.is_finite())
}

pub(crate) fn from_f32(f: f32) -> Option<Number> {
if f.is_finite() {
let n = {
#[cfg(not(feature = "arbitrary_precision"))]
{
N::Float(f as f64)
}
#[cfg(feature = "arbitrary_precision")]
{
ryu::Buffer::new().format_finite(f).to_owned()
}
};
Some(Number { n })
} else {
None
}
}

#[cfg(feature = "arbitrary_precision")]
/// Not public API. Only tests use this.
#[doc(hidden)]
Expand Down
2 changes: 1 addition & 1 deletion src/value/from.rs
Expand Up @@ -40,7 +40,7 @@ impl From<f32> for Value {
/// let x: Value = f.into();
/// ```
fn from(f: f32) -> Self {
From::from(f as f64)
Number::from_f32(f).map_or(Value::Null, Value::Number)
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/value/partial_eq.rs
Expand Up @@ -9,6 +9,13 @@ fn eq_u64(value: &Value, other: u64) -> bool {
value.as_u64().map_or(false, |i| i == other)
}

fn eq_f32(value: &Value, other: f32) -> bool {
match value {
Value::Number(n) => n.as_f32().map_or(false, |i| i == other),
_ => false,
}
}

fn eq_f64(value: &Value, other: f64) -> bool {
value.as_f64().map_or(false, |i| i == other)
}
Expand Down Expand Up @@ -90,6 +97,7 @@ macro_rules! partialeq_numeric {
partialeq_numeric! {
eq_i64[i8 i16 i32 i64 isize]
eq_u64[u8 u16 u32 u64 usize]
eq_f64[f32 f64]
eq_f32[f32]
eq_f64[f64]
eq_bool[bool]
}
9 changes: 4 additions & 5 deletions src/value/ser.rs
@@ -1,6 +1,5 @@
use crate::error::{Error, ErrorCode, Result};
use crate::map::Map;
use crate::number::Number;
use crate::value::{to_value, Value};
use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
Expand Down Expand Up @@ -149,13 +148,13 @@ impl serde::Serializer for Serializer {
}

#[inline]
fn serialize_f32(self, value: f32) -> Result<Value> {
self.serialize_f64(value as f64)
fn serialize_f32(self, float: f32) -> Result<Value> {
Ok(Value::from(float))
}

#[inline]
fn serialize_f64(self, value: f64) -> Result<Value> {
Ok(Number::from_f64(value).map_or(Value::Null, Value::Number))
fn serialize_f64(self, float: f64) -> Result<Value> {
Ok(Value::from(float))
}

#[inline]
Expand Down
12 changes: 12 additions & 0 deletions tests/regression/issue1004.rs
@@ -0,0 +1,12 @@
#![cfg(feature = "arbitrary_precision")]

#[test]
fn test() {
let float = 5.55f32;
let value = serde_json::to_value(&float).unwrap();
let json = serde_json::to_string(&value).unwrap();

// If the f32 were cast to f64 by Value before serialization, then this
// would incorrectly serialize as 5.550000190734863.
assert_eq!(json, "5.55");
}

0 comments on commit 731886c

Please sign in to comment.