1 //! Low-level API generator. 2 //! 3 //! Uses either the internal "Grisu2", or the external "Grisu3" or "Ryu" 4 //! algorithms provided by `https://github.com/dtolnay`. 5 6 // The following benchmarks were run on an "Intel(R) Core(TM) i7-6560U 7 // CPU @ 2.20GHz" CPU, on Fedora 28, Linux kernel version 4.18.16-200 8 // (x86-64), using the lexical formatter or `x.parse()`, 9 // avoiding any inefficiencies in Rust string parsing. The code was 10 // compiled with LTO and at an optimization level of 3. 11 // 12 // The benchmarks with `std` were compiled using "rustc 1.29.2 (17a9dc751 13 // 2018-10-05", and the `no_std` benchmarks were compiled using "rustc 14 // 1.31.0-nightly (46880f41b 2018-10-15)". 15 // 16 // The benchmark code may be found `benches/atof.rs`. 17 // 18 // # Benchmarks 19 // 20 // | Type | lexical (ns/iter) | libcore (ns/iter) | Relative Increase | 21 // |:-----:|:------------------:|:---------------------:|:-----------------:| 22 // | f32 | 465,584 | 1,884,646 | 4.04x | 23 // | f64 | 539,904 | 2,276,839 | 4.22x | 24 // 25 // # Raw Benchmarks 26 // 27 // ```text 28 // test ftoa_f32_dtoa ... bench: 917,561 ns/iter (+/- 45,458) 29 // test ftoa_f32_lexical ... bench: 465,584 ns/iter (+/- 76,158) 30 // test ftoa_f32_std ... bench: 1,884,646 ns/iter (+/- 130,721) 31 // test ftoa_f64_dtoa ... bench: 1,092,687 ns/iter (+/- 125,136) 32 // test ftoa_f64_lexical ... bench: 539,904 ns/iter (+/- 29,626) 33 // test ftoa_f64_std ... bench: 2,276,839 ns/iter (+/- 64,515) 34 // ``` 35 36 // Code the generate the benchmark plot: 37 // import numpy as np 38 // import pandas as pd 39 // import matplotlib.pyplot as plt 40 // plt.style.use('ggplot') 41 // lexical = np.array([465584, 539904]) / 1e6 42 // rustcore = np.array([1884646, 2276839]) / 1e6 43 // dtoa = np.array([917561, 1092687]) / 1e6 44 // ryu = np.array([432878, 522515]) / 1e6 45 // index = ["f32", "f64"] 46 // df = pd.DataFrame({'lexical': lexical, 'rustcore': rustcore, 'dtoa': dtoa, 'ryu': ryu}, index = index, columns=['lexical', 'dtoa', 'ryu', 'rustcore']) 47 // ax = df.plot.bar(rot=0, figsize=(16, 8), fontsize=14) 48 // ax.set_ylabel("ms/iter") 49 // ax.figure.tight_layout() 50 // ax.legend(loc=2, prop={'size': 14}) 51 // plt.show() 52 53 use crate::util::*; 54 55 #[cfg(feature = "radix")] 56 use super::radix::{double_radix, float_radix}; 57 58 // Select the back-end 59 cfg_if! { 60 if #[cfg(feature = "grisu3")] { 61 use super::grisu3::{double_decimal, float_decimal}; 62 } else if #[cfg(feature = "ryu")] { 63 use super::ryu::{double_decimal, float_decimal}; 64 } else { 65 use super::grisu2::{double_decimal, float_decimal}; 66 }} //cfg_if 67 68 // TRAITS 69 70 /// Trait to define serialization of a float to string. 71 pub(crate) trait FloatToString: Float { 72 /// Export float to decimal string with optimized algorithm. decimal<'a>(self, bytes: &'a mut [u8]) -> usize73 fn decimal<'a>(self, bytes: &'a mut [u8]) -> usize; 74 75 /// Export float to radix string with slow algorithm. 76 #[cfg(feature = "radix")] radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize77 fn radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize; 78 } 79 80 impl FloatToString for f32 { 81 perftools_inline!{ 82 fn decimal<'a>(self, bytes: &'a mut [u8]) -> usize { 83 float_decimal(self, bytes) 84 }} 85 86 perftools_inline!{ 87 #[cfg(feature = "radix")] 88 fn radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize { 89 float_radix(self, radix, bytes) 90 }} 91 } 92 93 impl FloatToString for f64 { 94 perftools_inline!{ 95 fn decimal<'a>(self, bytes: &'a mut [u8]) -> usize { 96 double_decimal(self, bytes) 97 }} 98 99 perftools_inline!{ 100 #[cfg(feature = "radix")] 101 fn radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize { 102 double_radix(self, radix, bytes) 103 }} 104 } 105 106 // FTOA 107 108 // Forward the correct arguments the ideal encoder. 109 perftools_inline!{ 110 fn forward<'a, F: FloatToString>(value: F, radix: u32, bytes: &'a mut [u8]) 111 -> usize 112 { 113 debug_assert_radix!(radix); 114 115 #[cfg(not(feature = "radix"))] { 116 value.decimal(bytes) 117 } 118 119 #[cfg(feature = "radix")] { 120 match radix { 121 10 => value.decimal(bytes), 122 _ => value.radix(radix, bytes), 123 } 124 } 125 }} 126 127 // Convert float-to-string and handle special (positive) floats. 128 perftools_inline!{ 129 fn filter_special<'a, F: FloatToString>(value: F, radix: u32, bytes: &'a mut [u8]) 130 -> usize 131 { 132 // Logic errors, disable in release builds. 133 debug_assert!(value.is_sign_positive(), "Value cannot be negative."); 134 debug_assert_radix!(radix); 135 136 // We already check for 0 in `filter_sign` if value.is_zero(). 137 #[cfg(not(feature = "trim_floats"))] { 138 if value.is_zero() { 139 // This is safe, because we confirmed the buffer is >= 4 140 // in total (since we also handled the sign by here). 141 return copy_to_dst(bytes, b"0.0"); 142 } 143 } 144 145 if value.is_nan() { 146 // This is safe, because we confirmed the buffer is >= F::FORMATTED_SIZE. 147 // We have up to `F::FORMATTED_SIZE - 1` bytes from `get_nan_string()`, 148 // and up to 1 byte from the sign. 149 copy_to_dst(bytes, get_nan_string()) 150 } else if value.is_special() { 151 // This is safe, because we confirmed the buffer is >= F::FORMATTED_SIZE. 152 // We have up to `F::FORMATTED_SIZE - 1` bytes from `get_inf_string()`, 153 // and up to 1 byte from the sign. 154 copy_to_dst(bytes, get_inf_string()) 155 } else { 156 forward(value, radix, bytes) 157 } 158 }} 159 160 // Handle +/- values. 161 perftools_inline!{ 162 fn filter_sign<'a, F: FloatToString>(value: F, radix: u32, bytes: &'a mut [u8]) 163 -> usize 164 { 165 debug_assert_radix!(radix); 166 167 // Export "-0.0" and "0.0" as "0" with trimmed floats. 168 #[cfg(feature = "trim_floats")] { 169 if value.is_zero() { 170 // We know this is safe, because we confirmed the buffer is >= 1. 171 index_mut!(bytes[0] = b'0'); 172 return 1; 173 } 174 } 175 176 // If the sign bit is set, invert it and just set the first 177 // value to "-". 178 if value.is_sign_negative() { 179 let value = -value; 180 // We know this is safe, because we confirmed the buffer is >= 1. 181 index_mut!(bytes[0] = b'-'); 182 let bytes = &mut index_mut!(bytes[1..]); 183 filter_special(value, radix, bytes) + 1 184 } else { 185 filter_special(value, radix, bytes) 186 } 187 }} 188 189 // Write float to string.. 190 perftools_inline!{ 191 fn ftoa<F: FloatToString>(value: F, radix: u32, bytes: &mut [u8]) 192 -> usize 193 { 194 let len = filter_sign(value, radix, bytes); 195 let bytes = &mut index_mut!(bytes[..len]); 196 trim(bytes) 197 }} 198 199 // Trim a trailing ".0" from a float. 200 perftools_inline!{ 201 fn trim<'a>(bytes: &'a mut [u8]) 202 -> usize 203 { 204 // Trim a trailing ".0" from a float. 205 if cfg!(feature = "trim_floats") && ends_with_slice(bytes, b".0") { 206 bytes.len() - 2 207 } else { 208 bytes.len() 209 } 210 }} 211 212 // TO LEXICAL 213 214 to_lexical!(ftoa, f32); 215 to_lexical!(ftoa, f64); 216 217 // TESTS 218 // ----- 219 220 #[cfg(test)] 221 mod tests { 222 use crate::util::*; 223 use crate::util::test::*; 224 225 #[cfg(all(feature = "correct", feature = "property_tests"))] 226 use quickcheck::quickcheck; 227 228 #[cfg(all(feature = "correct", feature = "std", feature = "property_tests"))] 229 use proptest::{proptest, prop_assert_eq}; 230 use approx::assert_relative_eq; 231 232 // Test data for roundtrips. 233 const F32_DATA : [f32; 31] = [0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e-8, 1.2345e-11, 1.2345e-38]; 234 const F64_DATA: [f64; 33] = [0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e+308, 1.2345e-8, 1.2345e-11, 1.2345e-38, 1.2345e-299]; 235 236 #[cfg(feature = "radix")] 237 #[test] f32_binary_test()238 fn f32_binary_test() { 239 let mut buffer = new_buffer(); 240 // positive 241 #[cfg(feature = "trim_floats")] { 242 assert_eq!(as_slice(b"0"), 0.0f32.to_lexical_radix(2, &mut buffer)); 243 assert_eq!(as_slice(b"0"), (-0.0f32).to_lexical_radix(2, &mut buffer)); 244 assert_eq!(as_slice(b"1"), 1.0f32.to_lexical_radix(2, &mut buffer)); 245 assert_eq!(as_slice(b"10"), 2.0f32.to_lexical_radix(2, &mut buffer)); 246 } 247 248 #[cfg(not(feature = "trim_floats"))] { 249 assert_eq!(as_slice(b"0.0"), 0.0f32.to_lexical_radix(2, &mut buffer)); 250 assert_eq!(as_slice(b"-0.0"), (-0.0f32).to_lexical_radix(2, &mut buffer)); 251 assert_eq!(as_slice(b"1.0"), 1.0f32.to_lexical_radix(2, &mut buffer)); 252 assert_eq!(as_slice(b"10.0"), 2.0f32.to_lexical_radix(2, &mut buffer)); 253 } 254 255 assert_eq!(as_slice(b"1.1"), 1.5f32.to_lexical_radix(2, &mut buffer)); 256 assert_eq!(as_slice(b"1.01"), 1.25f32.to_lexical_radix(2, &mut buffer)); 257 assert_eq!(b"1.001111000000110010", &1.2345678901234567890e0f32.to_lexical_radix(2, &mut buffer)[..20]); 258 assert_eq!(b"1100.010110000111111", &1.2345678901234567890e1f32.to_lexical_radix(2, &mut buffer)[..20]); 259 assert_eq!(b"1111011.011101001111", &1.2345678901234567890e2f32.to_lexical_radix(2, &mut buffer)[..20]); 260 assert_eq!(b"10011010010.10010001", &1.2345678901234567890e3f32.to_lexical_radix(2, &mut buffer)[..20]); 261 262 // negative 263 assert_eq!(b"-1.001111000000110010", &(-1.2345678901234567890e0f32).to_lexical_radix(2, &mut buffer)[..21]); 264 assert_eq!(b"-1100.010110000111111", &(-1.2345678901234567890e1f32).to_lexical_radix(2, &mut buffer)[..21]); 265 assert_eq!(b"-1111011.011101001111", &(-1.2345678901234567890e2f32).to_lexical_radix(2, &mut buffer)[..21]); 266 assert_eq!(b"-10011010010.10010001", &(-1.2345678901234567890e3f32).to_lexical_radix(2, &mut buffer)[..21]); 267 268 // special 269 assert_eq!(as_slice(b"NaN"), f32::NAN.to_lexical_radix(2, &mut buffer)); 270 assert_eq!(as_slice(b"inf"), f32::INFINITY.to_lexical_radix(2, &mut buffer)); 271 272 // bugfixes 273 assert_eq!(as_slice(b"1.1010100000101011110001e-11011"), 0.000000012345f32.to_lexical_radix(2, &mut buffer)); 274 } 275 276 #[test] f32_decimal_test()277 fn f32_decimal_test() { 278 let mut buffer = new_buffer(); 279 // positive 280 #[cfg(feature = "trim_floats")] { 281 assert_eq!(as_slice(b"0"), 0.0f32.to_lexical(&mut buffer)); 282 assert_eq!(as_slice(b"0"), (-0.0f32).to_lexical(&mut buffer)); 283 assert_eq!(as_slice(b"1"), 1.0f32.to_lexical(&mut buffer)); 284 assert_eq!(as_slice(b"10"), 10.0f32.to_lexical(&mut buffer)); 285 } 286 287 #[cfg(not(feature = "trim_floats"))] { 288 assert_eq!(as_slice(b"0.0"), 0.0f32.to_lexical(&mut buffer)); 289 assert_eq!(as_slice(b"-0.0"), (-0.0f32).to_lexical(&mut buffer)); 290 assert_eq!(as_slice(b"1.0"), 1.0f32.to_lexical(&mut buffer)); 291 assert_eq!(as_slice(b"10.0"), 10.0f32.to_lexical(&mut buffer)); 292 } 293 294 assert_eq!(as_slice(b"1.234567"), &1.2345678901234567890e0f32.to_lexical(&mut buffer)[..8]); 295 assert_eq!(as_slice(b"12.34567"), &1.2345678901234567890e1f32.to_lexical(&mut buffer)[..8]); 296 assert_eq!(as_slice(b"123.4567"), &1.2345678901234567890e2f32.to_lexical(&mut buffer)[..8]); 297 assert_eq!(as_slice(b"1234.567"), &1.2345678901234567890e3f32.to_lexical(&mut buffer)[..8]); 298 299 // negative 300 assert_eq!(as_slice(b"-1.234567"), &(-1.2345678901234567890e0f32).to_lexical(&mut buffer)[..9]); 301 assert_eq!(as_slice(b"-12.34567"), &(-1.2345678901234567890e1f32).to_lexical(&mut buffer)[..9]); 302 assert_eq!(as_slice(b"-123.4567"), &(-1.2345678901234567890e2f32).to_lexical(&mut buffer)[..9]); 303 assert_eq!(as_slice(b"-1234.567"), &(-1.2345678901234567890e3f32).to_lexical(&mut buffer)[..9]); 304 305 // special 306 assert_eq!(as_slice(b"NaN"), f32::NAN.to_lexical(&mut buffer)); 307 assert_eq!(as_slice(b"inf"), f32::INFINITY.to_lexical(&mut buffer)); 308 } 309 310 #[test] f32_decimal_roundtrip_test()311 fn f32_decimal_roundtrip_test() { 312 let mut buffer = new_buffer(); 313 for &f in F32_DATA.iter() { 314 let s = f.to_lexical(&mut buffer); 315 assert_relative_eq!(f32::from_lexical(s).unwrap(), f, epsilon=1e-6, max_relative=1e-6); 316 } 317 } 318 319 #[cfg(feature = "radix")] 320 #[test] f32_radix_roundtrip_test()321 fn f32_radix_roundtrip_test() { 322 let mut buffer = new_buffer(); 323 for &f in F32_DATA.iter() { 324 for radix in 2..37 { 325 // The lower accuracy is due to slight rounding errors of 326 // ftoa for the Grisu method with non-10 bases. 327 let s = f.to_lexical_radix(radix, &mut buffer); 328 assert_relative_eq!(f32::from_lexical_radix(s, radix).unwrap(), f, max_relative=2e-5); 329 } 330 } 331 } 332 333 #[cfg(feature = "radix")] 334 #[test] f64_binary_test()335 fn f64_binary_test() { 336 let mut buffer = new_buffer(); 337 // positive 338 #[cfg(feature = "trim_floats")] { 339 assert_eq!(as_slice(b"0"), 0.0f64.to_lexical_radix(2, &mut buffer)); 340 assert_eq!(as_slice(b"0"), (-0.0f64).to_lexical_radix(2, &mut buffer)); 341 assert_eq!(as_slice(b"1"), 1.0f64.to_lexical_radix(2, &mut buffer)); 342 assert_eq!(as_slice(b"10"), 2.0f64.to_lexical_radix(2, &mut buffer)); 343 } 344 345 #[cfg(not(feature = "trim_floats"))] { 346 assert_eq!(as_slice(b"0.0"), 0.0f64.to_lexical_radix(2, &mut buffer)); 347 assert_eq!(as_slice(b"-0.0"), (-0.0f64).to_lexical_radix(2, &mut buffer)); 348 assert_eq!(as_slice(b"1.0"), 1.0f64.to_lexical_radix(2, &mut buffer)); 349 assert_eq!(as_slice(b"10.0"), 2.0f64.to_lexical_radix(2, &mut buffer)); 350 } 351 352 assert_eq!(as_slice(b"1.00111100000011001010010000101000110001"), &1.2345678901234567890e0f64.to_lexical_radix(2, &mut buffer)[..40]); 353 assert_eq!(as_slice(b"1100.01011000011111100110100110010111101"), &1.2345678901234567890e1f64.to_lexical_radix(2, &mut buffer)[..40]); 354 assert_eq!(as_slice(b"1111011.01110100111100000001111111101101"), &1.2345678901234567890e2f64.to_lexical_radix(2, &mut buffer)[..40]); 355 assert_eq!(as_slice(b"10011010010.1001000101100001001111110100"), &1.2345678901234567890e3f64.to_lexical_radix(2, &mut buffer)[..40]); 356 357 // negative 358 assert_eq!(as_slice(b"-1.00111100000011001010010000101000110001"), &(-1.2345678901234567890e0f64).to_lexical_radix(2, &mut buffer)[..41]); 359 assert_eq!(as_slice(b"-1100.01011000011111100110100110010111101"), &(-1.2345678901234567890e1f64).to_lexical_radix(2, &mut buffer)[..41]); 360 assert_eq!(as_slice(b"-1111011.01110100111100000001111111101101"), &(-1.2345678901234567890e2f64).to_lexical_radix(2, &mut buffer)[..41]); 361 assert_eq!(as_slice(b"-10011010010.1001000101100001001111110100"), &(-1.2345678901234567890e3f64).to_lexical_radix(2, &mut buffer)[..41]); 362 363 // special 364 assert_eq!(as_slice(b"NaN"), f64::NAN.to_lexical_radix(2, &mut buffer)); 365 assert_eq!(as_slice(b"inf"), f64::INFINITY.to_lexical_radix(2, &mut buffer)); 366 } 367 368 #[test] f64_decimal_test()369 fn f64_decimal_test() { 370 let mut buffer = new_buffer(); 371 // positive 372 #[cfg(feature = "trim_floats")] { 373 assert_eq!(as_slice(b"0"), 0.0.to_lexical(&mut buffer)); 374 assert_eq!(as_slice(b"0"), (-0.0).to_lexical(&mut buffer)); 375 assert_eq!(as_slice(b"1"), 1.0.to_lexical(&mut buffer)); 376 assert_eq!(as_slice(b"10"), 10.0.to_lexical(&mut buffer)); 377 } 378 379 #[cfg(not(feature = "trim_floats"))] { 380 assert_eq!(as_slice(b"0.0"), 0.0.to_lexical(&mut buffer)); 381 assert_eq!(as_slice(b"-0.0"), (-0.0).to_lexical(&mut buffer)); 382 assert_eq!(as_slice(b"1.0"), 1.0.to_lexical(&mut buffer)); 383 assert_eq!(as_slice(b"10.0"), 10.0.to_lexical(&mut buffer)); 384 } 385 386 assert_eq!(as_slice(b"1.234567"), &1.2345678901234567890e0.to_lexical(&mut buffer)[..8]); 387 assert_eq!(as_slice(b"12.34567"), &1.2345678901234567890e1.to_lexical(&mut buffer)[..8]); 388 assert_eq!(as_slice(b"123.4567"), &1.2345678901234567890e2.to_lexical(&mut buffer)[..8]); 389 assert_eq!(as_slice(b"1234.567"), &1.2345678901234567890e3.to_lexical(&mut buffer)[..8]); 390 391 // negative 392 assert_eq!(as_slice(b"-1.234567"), &(-1.2345678901234567890e0).to_lexical(&mut buffer)[..9]); 393 assert_eq!(as_slice(b"-12.34567"), &(-1.2345678901234567890e1).to_lexical(&mut buffer)[..9]); 394 assert_eq!(as_slice(b"-123.4567"), &(-1.2345678901234567890e2).to_lexical(&mut buffer)[..9]); 395 assert_eq!(as_slice(b"-1234.567"), &(-1.2345678901234567890e3).to_lexical(&mut buffer)[..9]); 396 397 // special 398 assert_eq!(b"NaN".to_vec(), f64::NAN.to_lexical(&mut buffer)); 399 assert_eq!(b"inf".to_vec(), f64::INFINITY.to_lexical(&mut buffer)); 400 } 401 402 #[test] f64_decimal_roundtrip_test()403 fn f64_decimal_roundtrip_test() { 404 let mut buffer = new_buffer(); 405 for &f in F64_DATA.iter() { 406 let s = f.to_lexical(&mut buffer); 407 assert_relative_eq!(f64::from_lexical(s).unwrap(), f, epsilon=1e-12, max_relative=1e-12); 408 } 409 } 410 411 #[cfg(feature = "radix")] 412 #[test] f64_radix_roundtrip_test()413 fn f64_radix_roundtrip_test() { 414 let mut buffer = new_buffer(); 415 for &f in F64_DATA.iter() { 416 for radix in 2..37 { 417 // The lower accuracy is due to slight rounding errors of 418 // ftoa for the Grisu method with non-10 bases. 419 let s = f.to_lexical_radix(radix, &mut buffer); 420 assert_relative_eq!(f64::from_lexical_radix(s, radix).unwrap(), f, max_relative=3e-5); 421 } 422 } 423 } 424 425 #[cfg(all(feature = "correct", feature = "property_tests"))] 426 quickcheck! { 427 fn f32_quickcheck(f: f32) -> bool { 428 let mut buffer = new_buffer(); 429 let parsed = f32::from_lexical(f.to_lexical(&mut buffer)).unwrap(); 430 if f.is_nan() { 431 parsed.is_nan() 432 } else { 433 f == parsed 434 } 435 } 436 437 fn f64_quickcheck(f: f64) -> bool { 438 let mut buffer = new_buffer(); 439 let parsed = f64::from_lexical(f.to_lexical(&mut buffer)).unwrap(); 440 if f.is_nan() { 441 parsed.is_nan() 442 } else { 443 f == parsed 444 } 445 } 446 } 447 448 #[cfg(all(feature = "correct", feature = "std", feature = "property_tests"))] 449 proptest! { 450 #[test] 451 fn f32_proptest(i in f32::MIN..f32::MAX) { 452 let mut buffer = new_buffer(); 453 prop_assert_eq!(i, f32::from_lexical(i.to_lexical(&mut buffer)).unwrap()); 454 } 455 456 #[test] 457 fn f64_proptest(i in f64::MIN..f64::MAX) { 458 let mut buffer = new_buffer(); 459 prop_assert_eq!(i, f64::from_lexical(i.to_lexical(&mut buffer)).unwrap()); 460 } 461 } 462 463 #[test] 464 #[should_panic] f32_buffer_test()465 fn f32_buffer_test() { 466 let mut buffer = [b'0'; f32::FORMATTED_SIZE_DECIMAL-1]; 467 1.2345f32.to_lexical(&mut buffer); 468 } 469 470 #[test] 471 #[should_panic] f64_buffer_test()472 fn f64_buffer_test() { 473 let mut buffer = [b'0'; f64::FORMATTED_SIZE_DECIMAL-1]; 474 1.2345f64.to_lexical(&mut buffer); 475 } 476 } 477