/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-short-0.3.5/src/lib.rs
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #![no_std] |
6 | | |
7 | | extern crate dtoa; |
8 | | |
9 | | use core::fmt::Write; |
10 | | use core::{fmt, str}; |
11 | | |
12 | | /// Format the given `value` into `dest` and return the notation it uses. |
13 | | #[inline] |
14 | 0 | pub fn write<W: Write, V: Floating>(dest: &mut W, value: V) -> DtoaResult { |
15 | 0 | Floating::write(value, dest) |
16 | 0 | } Unexecuted instantiation: dtoa_short::write::<alloc::string::String, f32> Unexecuted instantiation: dtoa_short::write::<_, _> |
17 | | |
18 | | /// Form of the formatted floating-point number. |
19 | | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
20 | | pub struct Notation { |
21 | | /// Whether it contains a decimal point. |
22 | | pub decimal_point: bool, |
23 | | /// Whether it uses E-notation. |
24 | | pub scientific: bool, |
25 | | } |
26 | | |
27 | | impl Notation { |
28 | 0 | fn integer() -> Self { |
29 | 0 | Notation { |
30 | 0 | decimal_point: false, |
31 | 0 | scientific: false, |
32 | 0 | } |
33 | 0 | } |
34 | | } |
35 | | |
36 | | /// Result of formatting the number. |
37 | | pub type DtoaResult = Result<Notation, fmt::Error>; |
38 | | |
39 | | pub trait Floating : dtoa::Float { |
40 | | fn write<W: Write>(self, dest: &mut W) -> DtoaResult; |
41 | | } |
42 | | |
43 | | impl Floating for f32 { |
44 | 0 | fn write<W: Write>(self, dest: &mut W) -> DtoaResult { |
45 | 0 | write_with_prec(dest, self, 6) |
46 | 0 | } Unexecuted instantiation: <f32 as dtoa_short::Floating>::write::<alloc::string::String> Unexecuted instantiation: <f32 as dtoa_short::Floating>::write::<_> |
47 | | } |
48 | | |
49 | | impl Floating for f64 { |
50 | 0 | fn write<W: Write>(self, dest: &mut W) -> DtoaResult { |
51 | 0 | write_with_prec(dest, self, 15) |
52 | 0 | } |
53 | | } |
54 | | |
55 | 0 | fn write_with_prec<W, V>(dest: &mut W, value: V, prec: usize) -> DtoaResult |
56 | 0 | where |
57 | 0 | W: Write, |
58 | 0 | V: dtoa::Float |
59 | | { |
60 | 0 | let mut buf = dtoa::Buffer::new(); |
61 | 0 | let str = buf.format_finite(value); |
62 | | |
63 | | const SCRATCH_LEN: usize = core::mem::size_of::<dtoa::Buffer>() + 1; |
64 | 0 | let mut scratch = [b'\0'; SCRATCH_LEN]; |
65 | 0 | debug_assert!(str.len() < SCRATCH_LEN); |
66 | 0 | unsafe { |
67 | 0 | core::ptr::copy_nonoverlapping(str.as_bytes().as_ptr(), scratch.as_mut_ptr().offset(1), str.len()); |
68 | 0 | } |
69 | 0 | let (result, notation) = restrict_prec(&mut scratch[0..str.len() + 1], prec); |
70 | 0 | dest.write_str(if cfg!(debug_assertions) { |
71 | 0 | str::from_utf8(result).unwrap() |
72 | | } else { |
73 | | // safety: dtoa only generates ascii. |
74 | 0 | unsafe { str::from_utf8_unchecked(result) } |
75 | 0 | })?; |
76 | 0 | Ok(notation) |
77 | 0 | } Unexecuted instantiation: dtoa_short::write_with_prec::<alloc::string::String, f32> Unexecuted instantiation: dtoa_short::write_with_prec::<_, _> |
78 | | |
79 | 0 | fn restrict_prec(buf: &mut [u8], prec: usize) -> (&[u8], Notation) { |
80 | 0 | let len = buf.len(); |
81 | | // Put a leading zero to capture any carry. |
82 | 0 | debug_assert!(buf[0] == b'\0', "Caller must prepare an empty byte for us"); |
83 | 0 | buf[0] = b'0'; |
84 | | // Remove the sign for now. We will put it back at the end. |
85 | 0 | let sign = match buf[1] { |
86 | 0 | s @ b'+' | s @ b'-' => { |
87 | 0 | buf[1] = b'0'; |
88 | 0 | Some(s) |
89 | | } |
90 | 0 | _ => None, |
91 | | }; |
92 | | // Locate dot, exponent, and the first significant digit. |
93 | 0 | let mut pos_dot = None; |
94 | 0 | let mut pos_exp = None; |
95 | 0 | let mut prec_start = None; |
96 | 0 | for i in 1..len { |
97 | 0 | if buf[i] == b'.' { |
98 | 0 | debug_assert!(pos_dot.is_none()); |
99 | 0 | pos_dot = Some(i); |
100 | 0 | } else if buf[i] == b'e' { |
101 | 0 | pos_exp = Some(i); |
102 | | // We don't change exponent part, so stop here. |
103 | 0 | break; |
104 | 0 | } else if prec_start.is_none() && buf[i] != b'0' { |
105 | 0 | debug_assert!(buf[i] >= b'1' && buf[i] <= b'9'); |
106 | 0 | prec_start = Some(i); |
107 | 0 | } |
108 | | } |
109 | 0 | let prec_start = match prec_start { |
110 | 0 | Some(i) => i, |
111 | | // If there is no non-zero digit at all, it is just zero. |
112 | 0 | None => return (&buf[0..1], Notation::integer()), |
113 | | }; |
114 | | // Coefficient part ends at 'e' or the length. |
115 | 0 | let coeff_end = pos_exp.unwrap_or(len); |
116 | | // Decimal dot is effectively at the end of coefficient part if no |
117 | | // dot presents before that. |
118 | 0 | let pos_dot = pos_dot.unwrap_or(coeff_end); |
119 | | // Find the end position of the number within the given precision. |
120 | 0 | let prec_end = { |
121 | 0 | let end = prec_start + prec; |
122 | 0 | if pos_dot > prec_start && pos_dot <= end { |
123 | 0 | end + 1 |
124 | | } else { |
125 | 0 | end |
126 | | } |
127 | | }; |
128 | 0 | let mut new_coeff_end = coeff_end; |
129 | 0 | if prec_end < coeff_end { |
130 | | // Round to the given precision. |
131 | 0 | let next_char = buf[prec_end]; |
132 | 0 | new_coeff_end = prec_end; |
133 | 0 | if next_char >= b'5' { |
134 | 0 | for i in (0..prec_end).rev() { |
135 | 0 | if buf[i] == b'.' { |
136 | 0 | continue; |
137 | 0 | } |
138 | 0 | if buf[i] != b'9' { |
139 | 0 | buf[i] += 1; |
140 | 0 | new_coeff_end = i + 1; |
141 | 0 | break; |
142 | 0 | } |
143 | 0 | buf[i] = b'0'; |
144 | | } |
145 | 0 | } |
146 | 0 | } |
147 | 0 | if new_coeff_end < pos_dot { |
148 | | // If the precision isn't enough to reach the dot, set all digits |
149 | | // in-between to zero and keep the number until the dot. |
150 | 0 | for i in new_coeff_end..pos_dot { |
151 | 0 | buf[i] = b'0'; |
152 | 0 | } |
153 | 0 | new_coeff_end = pos_dot; |
154 | | } else { |
155 | | // Strip any trailing zeros. |
156 | 0 | for i in (0..new_coeff_end).rev() { |
157 | 0 | if buf[i] != b'0' { |
158 | 0 | if buf[i] == b'.' { |
159 | 0 | new_coeff_end = i; |
160 | 0 | } |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | new_coeff_end = i; |
164 | | } |
165 | | } |
166 | | // Move exponent part if necessary. |
167 | 0 | let real_end = if let Some(pos_exp) = pos_exp { |
168 | 0 | let exp_len = len - pos_exp; |
169 | 0 | if new_coeff_end != pos_exp { |
170 | 0 | for i in 0..exp_len { |
171 | 0 | buf[new_coeff_end + i] = buf[pos_exp + i]; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | new_coeff_end + exp_len |
175 | | } else { |
176 | 0 | new_coeff_end |
177 | | }; |
178 | | // Add back the sign and strip the leading zero. |
179 | 0 | let result = if let Some(sign) = sign { |
180 | 0 | if buf[1] == b'0' && buf[2] != b'.' { |
181 | 0 | buf[1] = sign; |
182 | 0 | &buf[1..real_end] |
183 | | } else { |
184 | 0 | debug_assert!(buf[0] == b'0'); |
185 | 0 | buf[0] = sign; |
186 | 0 | &buf[0..real_end] |
187 | | } |
188 | | } else { |
189 | 0 | if buf[0] == b'0' && buf[1] != b'.' { |
190 | 0 | &buf[1..real_end] |
191 | | } else { |
192 | 0 | &buf[0..real_end] |
193 | | } |
194 | | }; |
195 | | // Generate the notation info. |
196 | 0 | let notation = Notation { |
197 | 0 | decimal_point: pos_dot < new_coeff_end, |
198 | 0 | scientific: pos_exp.is_some(), |
199 | 0 | }; |
200 | 0 | (result, notation) |
201 | 0 | } |