core_json_traits/
float.rs1use core::num::FpCategory;
2use crate::{BytesLike, Stack, JsonError, Value, JsonDeserialize, JsonSerialize};
3
4impl JsonDeserialize for f64 {
5 fn deserialize<'bytes, 'parent, B: BytesLike<'bytes>, S: Stack>(
6 value: Value<'bytes, 'parent, B, S>,
7 ) -> Result<Self, JsonError<'bytes, B, S>> {
8 value.as_f64()
9 }
10}
11
12#[derive(Clone, Copy, Default, Debug)]
18pub struct JsonF64(f64);
19
20impl TryFrom<f64> for JsonF64 {
21 type Error = FpCategory;
22 fn try_from(value: f64) -> Result<Self, Self::Error> {
23 let class = value.classify();
24 match class {
25 FpCategory::Nan | FpCategory::Infinite => Err(class)?,
26 FpCategory::Zero | FpCategory::Normal | FpCategory::Subnormal => {}
27 }
28 Ok(Self(value))
29 }
30}
31
32impl From<JsonF64> for f64 {
33 fn from(value: JsonF64) -> f64 {
34 value.0
35 }
36}
37
38impl JsonDeserialize for JsonF64 {
39 fn deserialize<'bytes, 'parent, B: BytesLike<'bytes>, S: Stack>(
40 value: Value<'bytes, 'parent, B, S>,
41 ) -> Result<Self, JsonError<'bytes, B, S>> {
42 JsonF64::try_from(f64::deserialize(value)?).map_err(|_| JsonError::TypeError)
43 }
44}
45
46#[cfg(not(feature = "ryu"))]
47mod serialize {
48 use core::fmt::Write;
49 use super::*;
50
51 struct Buffer {
53 bytes: [u8; 1 + (f64::DIGITS as usize) + 1],
56 i: usize,
57 digits: usize,
58 before_decimal: bool,
59 omitted_decimal: bool,
60 before_exponent: bool,
61 negative_exponent: bool,
62 exponent: i16,
63 exponent_correction: i16,
64 }
65 impl Write for Buffer {
66 fn write_str(&mut self, value: &str) -> core::fmt::Result {
67 for char in value.chars() {
68 if self.before_exponent {
69 if char.is_ascii_digit() {
70 if self.digits == (f64::DIGITS as usize) {
73 if self.before_decimal {
75 self.exponent_correction += 1;
76 }
77 continue;
78 }
79
80 if self.omitted_decimal {
82 self.exponent_correction -= 1;
83 }
84
85 if (self.digits == 0) && (char == '0') {
87 continue;
88 }
89
90 self.digits += 1;
92 }
93
94 if char == '.' {
95 self.before_decimal = false;
96 if self.digits == 0 {
98 self.omitted_decimal = true;
99 continue;
100 }
101 if self.digits == (f64::DIGITS as usize) {
103 continue;
104 }
105 }
106 }
107
108 if matches!(char, 'e' | 'E') {
109 self.before_exponent = false;
110 continue;
111 }
112
113 if self.before_exponent {
114 self.bytes[self.i] = char as u8;
116 self.i += 1;
117 } else {
118 if char == '+' {
121 continue;
122 }
123 if char == '-' {
124 self.negative_exponent = true;
125 continue;
126 }
127 self.exponent *= 10;
128 self.exponent += i16::from((char as u8) - b'0');
129 }
130 }
131 Ok(())
132 }
133 }
134
135 impl JsonSerialize for JsonF64 {
136 fn serialize(&self) -> impl Iterator<Item = char> {
138 let mut buffer = Buffer {
139 bytes: [b'0'; _],
140 i: 0,
141 digits: 0,
142 before_decimal: true,
143 omitted_decimal: false,
144 before_exponent: true,
145 negative_exponent: false,
146 exponent: 0,
147 exponent_correction: 0,
148 };
149 write!(&mut buffer, "{:?}", self.0).expect("infallible buffer raised an error");
150
151 if buffer.i.checked_sub(1).map(|i| buffer.bytes[i]) == Some(b'.') {
153 buffer.i -= 1;
154 }
155
156 let exponent = {
157 let exponent = (if buffer.negative_exponent { -buffer.exponent } else { buffer.exponent }) +
158 buffer.exponent_correction;
159
160 ((buffer.i != 0) && (exponent != 0))
161 .then(|| core::iter::once('e').chain(crate::primitives::i64_to_str(exponent)))
162 .into_iter()
163 .flatten()
164 };
165 buffer.bytes.into_iter().take(buffer.i.max(1)).map(|b| b as char).chain(exponent)
167 }
168 }
169
170 #[test]
171 fn f64_serialize() {
172 use core::str::FromStr;
173 #[allow(clippy::float_cmp)]
174 let test = |value: f64, expected| {
175 assert_eq!(
176 f64::from_str(&JsonF64::try_from(value).unwrap().serialize().collect::<String>()).unwrap(),
177 f64::from_str(expected).unwrap()
178 );
179 };
180 test(0.0, "0");
181 test(0.1, "1e-1");
182 test(0.01, "1e-2");
183 test(0.001, "1e-3");
184 test(0.0012, "12e-4");
185 test(0.12345678910111213, "123456789101112e-15");
186 test(0.012345678910111213, "123456789101112e-16");
187 test(12345678910111213.0, "123456789101112e2");
188 test(12345678910111213.123, "123456789101112e2");
189 test(123456789.101112, "123456789.101112");
190 test(123456789.10111213, "123456789.101112");
191 test(-1.0, "-1");
192 test(f64::MIN, "-179769313486231e294");
193 test(f64::MAX, "179769313486231e294");
194 test(f64::EPSILON, "222044604925031e-30");
195 }
196}
197
198#[cfg(feature = "ryu")]
199mod serialize {
200 use super::*;
201
202 impl JsonSerialize for JsonF64 {
203 fn serialize(&self) -> impl Iterator<Item = char> {
204 let mut buffer = ryu::Buffer::new();
205 let result = buffer.format_finite(self.0).as_bytes();
207 let mut owned = [0; core::mem::size_of::<ryu::Buffer>()];
213 owned[.. result.len()].copy_from_slice(result);
214 owned.into_iter().take(result.len()).map(|byte| byte as char)
216 }
217 }
218}