summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Blachman <blachmanmax@gmail.com>2019-11-24 14:30:56 -0800
committerMax Blachman <blachmanmax@gmail.com>2020-02-14 18:42:51 -0800
commita8fe47d1756df0fb68f6ed2edd2cedfb3cc66d7c (patch)
treef2b530b71e94bc1836f64afef592b259133013a9
parentAuto merge of #69172 - JohnTitor:rollup-6cbmwcw, r=JohnTitor (diff)
downloadrust-a8fe47d1756df0fb68f6ed2edd2cedfb3cc66d7c.tar.gz
rust-a8fe47d1756df0fb68f6ed2edd2cedfb3cc66d7c.tar.bz2
rust-a8fe47d1756df0fb68f6ed2edd2cedfb3cc66d7c.tar.xz
implement LowerExp and UpperExp for integers
-rw-r--r--src/libcore/fmt/num.rs163
-rw-r--r--src/libcore/tests/fmt/num.rs80
2 files changed, 243 insertions, 0 deletions
diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs
index d562639..c187471 100644
--- a/src/libcore/fmt/num.rs
+++ b/src/libcore/fmt/num.rs
@@ -4,6 +4,7 @@
4 4
5use crate::fmt; 5use crate::fmt;
6use crate::mem::MaybeUninit; 6use crate::mem::MaybeUninit;
7use crate::num::flt2dec;
7use crate::ops::{Div, Rem, Sub}; 8use crate::ops::{Div, Rem, Sub};
8use crate::ptr; 9use crate::ptr;
9use crate::slice; 10use crate::slice;
@@ -256,6 +257,161 @@ macro_rules! impl_Display {
256 }; 257 };
257} 258}
258 259
260macro_rules! impl_Exp {
261 ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
262 fn $name(
263 mut n: $u,
264 is_nonnegative: bool,
265 upper: bool,
266 f: &mut fmt::Formatter<'_>
267 ) -> fmt::Result {
268 let (mut n, mut exponent, trailing_zeros, added_precision) = {
269 let mut exponent = 0;
270 // count and remove trailing decimal zeroes
271 while n % 10 == 0 && n >= 10 {
272 n /= 10;
273 exponent += 1;
274 }
275 let trailing_zeros = exponent;
276
277 let (added_precision, subtracted_precision) = match f.precision() {
278 Some(fmt_prec) => {
279 // number of decimal digits minus 1
280 let mut tmp = n;
281 let mut prec = 0;
282 while tmp >= 10 {
283 tmp /= 10;
284 prec += 1;
285 }
286 (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec))
287 }
288 None => (0,0)
289 };
290 for _ in 1..subtracted_precision {
291 n/=10;
292 exponent += 1;
293 }
294 if subtracted_precision != 0 {
295 let rem = n % 10;
296 n /= 10;
297 exponent += 1;
298 // round up last digit
299 if rem >= 5 {
300 n += 1;
301 }
302 }
303 (n, exponent, trailing_zeros, added_precision)
304 };
305
306 // 39 digits (worst case u128) + . = 40
307 let mut buf = [MaybeUninit::<u8>::uninit(); 40];
308 let mut curr = buf.len() as isize; //index for buf
309 let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
310 let lut_ptr = DEC_DIGITS_LUT.as_ptr();
311
312 // decode 2 chars at a time
313 while n >= 100 {
314 let d1 = ((n % 100) as isize) << 1;
315 curr -= 2;
316 unsafe {
317 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
318 }
319 n /= 100;
320 exponent += 2;
321 }
322 // n is <= 99, so at most 2 chars long
323 let mut n = n as isize; // possibly reduce 64bit math
324 // decode second-to-last character
325 if n >= 10 {
326 curr -= 1;
327 unsafe {
328 *buf_ptr.offset(curr) = (n as u8 % 10_u8) + b'0';
329 }
330 n /= 10;
331 exponent += 1;
332 }
333 // add decimal point iff >1 mantissa digit will be printed
334 if exponent != trailing_zeros || added_precision != 0 {
335 curr -= 1;
336 unsafe {
337 *buf_ptr.offset(curr) = b'.';
338 }
339 }
340
341 let buf_slice = unsafe {
342 // decode last character
343 curr -= 1;
344 *buf_ptr.offset(curr) = (n as u8) + b'0';
345
346 let len = buf.len() - curr as usize;
347 slice::from_raw_parts(buf_ptr.offset(curr), len)
348 };
349
350 // stores 'e' (or 'E') and the up to 2-digit exponent
351 let mut exp_buf = [MaybeUninit::<u8>::uninit(); 3];
352 let exp_ptr = MaybeUninit::first_ptr_mut(&mut exp_buf);
353 let exp_slice = unsafe {
354 *exp_ptr.offset(0) = if upper {b'E'} else {b'e'};
355 let len = if exponent < 10 {
356 *exp_ptr.offset(1) = (exponent as u8) + b'0';
357 2
358 } else {
359 let off = exponent << 1;
360 ptr::copy_nonoverlapping(lut_ptr.offset(off), exp_ptr.offset(1), 2);
361 3
362 };
363 slice::from_raw_parts(exp_ptr, len)
364 };
365
366 let parts = &[
367 flt2dec::Part::Copy(buf_slice),
368 flt2dec::Part::Zero(added_precision),
369 flt2dec::Part::Copy(exp_slice)
370 ];
371 let sign = if !is_nonnegative {
372 &b"-"[..]
373 } else if f.sign_plus() {
374 &b"+"[..]
375 } else {
376 &b""[..]
377 };
378 let formatted = flt2dec::Formatted{sign, parts};
379 f.pad_formatted_parts(&formatted)
380 }
381
382 $(
383 #[stable(feature = "integer_exp_format", since = "1.42.0")]
384 impl fmt::LowerExp for $t {
385 #[allow(unused_comparisons)]
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 let is_nonnegative = *self >= 0;
388 let n = if is_nonnegative {
389 self.$conv_fn()
390 } else {
391 // convert the negative num to positive by summing 1 to it's 2 complement
392 (!self.$conv_fn()).wrapping_add(1)
393 };
394 $name(n, is_nonnegative, false, f)
395 }
396 })*
397 $(
398 #[stable(feature = "integer_exp_format", since = "1.42.0")]
399 impl fmt::UpperExp for $t {
400 #[allow(unused_comparisons)]
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 let is_nonnegative = *self >= 0;
403 let n = if is_nonnegative {
404 self.$conv_fn()
405 } else {
406 // convert the negative num to positive by summing 1 to it's 2 complement
407 (!self.$conv_fn()).wrapping_add(1)
408 };
409 $name(n, is_nonnegative, true, f)
410 }
411 })*
412 };
413}
414
259// Include wasm32 in here since it doesn't reflect the native pointer size, and 415// Include wasm32 in here since it doesn't reflect the native pointer size, and
260// often cares strongly about getting a smaller code size. 416// often cares strongly about getting a smaller code size.
261#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] 417#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
@@ -265,6 +421,10 @@ mod imp {
265 i8, u8, i16, u16, i32, u32, i64, u64, usize, isize 421 i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
266 as u64 via to_u64 named fmt_u64 422 as u64 via to_u64 named fmt_u64
267 ); 423 );
424 impl_Exp!(
425 i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
426 as u64 via to_u64 named exp_u64
427 );
268} 428}
269 429
270#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] 430#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
@@ -272,6 +432,9 @@ mod imp {
272 use super::*; 432 use super::*;
273 impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32); 433 impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32);
274 impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64); 434 impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64);
435 impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32);
436 impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64);
275} 437}
276 438
277impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128); 439impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128);
440impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
diff --git a/src/libcore/tests/fmt/num.rs b/src/libcore/tests/fmt/num.rs
index 10fcf8b..a50c2b4 100644
--- a/src/libcore/tests/fmt/num.rs
+++ b/src/libcore/tests/fmt/num.rs
@@ -38,6 +38,16 @@ fn test_format_int() {
38 assert_eq!(format!("{:o}", 1i16), "1"); 38 assert_eq!(format!("{:o}", 1i16), "1");
39 assert_eq!(format!("{:o}", 1i32), "1"); 39 assert_eq!(format!("{:o}", 1i32), "1");
40 assert_eq!(format!("{:o}", 1i64), "1"); 40 assert_eq!(format!("{:o}", 1i64), "1");
41 assert_eq!(format!("{:e}", 1isize), "1e0");
42 assert_eq!(format!("{:e}", 1i8), "1e0");
43 assert_eq!(format!("{:e}", 1i16), "1e0");
44 assert_eq!(format!("{:e}", 1i32), "1e0");
45 assert_eq!(format!("{:e}", 1i64), "1e0");
46 assert_eq!(format!("{:E}", 1isize), "1E0");
47 assert_eq!(format!("{:E}", 1i8), "1E0");
48 assert_eq!(format!("{:E}", 1i16), "1E0");
49 assert_eq!(format!("{:E}", 1i32), "1E0");
50 assert_eq!(format!("{:E}", 1i64), "1E0");
41 51
42 assert_eq!(format!("{}", 1usize), "1"); 52 assert_eq!(format!("{}", 1usize), "1");
43 assert_eq!(format!("{}", 1u8), "1"); 53 assert_eq!(format!("{}", 1u8), "1");
@@ -69,6 +79,14 @@ fn test_format_int() {
69 assert_eq!(format!("{:o}", 1u16), "1"); 79 assert_eq!(format!("{:o}", 1u16), "1");
70 assert_eq!(format!("{:o}", 1u32), "1"); 80 assert_eq!(format!("{:o}", 1u32), "1");
71 assert_eq!(format!("{:o}", 1u64), "1"); 81 assert_eq!(format!("{:o}", 1u64), "1");
82 assert_eq!(format!("{:e}", 1u8), "1e0");
83 assert_eq!(format!("{:e}", 1u16), "1e0");
84 assert_eq!(format!("{:e}", 1u32), "1e0");
85 assert_eq!(format!("{:e}", 1u64), "1e0");
86 assert_eq!(format!("{:E}", 1u8), "1E0");
87 assert_eq!(format!("{:E}", 1u16), "1E0");
88 assert_eq!(format!("{:E}", 1u32), "1E0");
89 assert_eq!(format!("{:E}", 1u64), "1E0");
72 90
73 // Test a larger number 91 // Test a larger number
74 assert_eq!(format!("{:b}", 55), "110111"); 92 assert_eq!(format!("{:b}", 55), "110111");
@@ -76,6 +94,64 @@ fn test_format_int() {
76 assert_eq!(format!("{}", 55), "55"); 94 assert_eq!(format!("{}", 55), "55");
77 assert_eq!(format!("{:x}", 55), "37"); 95 assert_eq!(format!("{:x}", 55), "37");
78 assert_eq!(format!("{:X}", 55), "37"); 96 assert_eq!(format!("{:X}", 55), "37");
97 assert_eq!(format!("{:e}", 55), "5.5e1");
98 assert_eq!(format!("{:E}", 55), "5.5E1");
99 assert_eq!(format!("{:e}", 10000000000u64), "1e10");
100 assert_eq!(format!("{:E}", 10000000000u64), "1E10");
101 assert_eq!(format!("{:e}", 10000000001u64), "1.0000000001e10");
102 assert_eq!(format!("{:E}", 10000000001u64), "1.0000000001E10");
103}
104
105#[test]
106fn test_format_int_exp_limits() {
107 use core::{i128, i16, i32, i64, i8, u128, u16, u32, u64, u8};
108 assert_eq!(format!("{:e}", i8::MIN), "-1.28e2");
109 assert_eq!(format!("{:e}", i8::MAX), "1.27e2");
110 assert_eq!(format!("{:e}", i16::MIN), "-3.2768e4");
111 assert_eq!(format!("{:e}", i16::MAX), "3.2767e4");
112 assert_eq!(format!("{:e}", i32::MIN), "-2.147483648e9");
113 assert_eq!(format!("{:e}", i32::MAX), "2.147483647e9");
114 assert_eq!(format!("{:e}", i64::MIN), "-9.223372036854775808e18");
115 assert_eq!(format!("{:e}", i64::MAX), "9.223372036854775807e18");
116 assert_eq!(format!("{:e}", i128::MIN), "-1.70141183460469231731687303715884105728e38");
117 assert_eq!(format!("{:e}", i128::MAX), "1.70141183460469231731687303715884105727e38");
118
119 assert_eq!(format!("{:e}", u8::MAX), "2.55e2");
120 assert_eq!(format!("{:e}", u16::MAX), "6.5535e4");
121 assert_eq!(format!("{:e}", u32::MAX), "4.294967295e9");
122 assert_eq!(format!("{:e}", u64::MAX), "1.8446744073709551615e19");
123 assert_eq!(format!("{:e}", u128::MAX), "3.40282366920938463463374607431768211455e38");
124}
125
126#[test]
127fn test_format_int_exp_precision() {
128 use core::{i128, i16, i32, i64, i8};
129
130 //test that float and integer match
131 let big_int: u32 = 314_159_265;
132 assert_eq!(format!("{:.1e}", big_int), format!("{:.1e}", f64::from(big_int)));
133
134 //test adding precision
135 assert_eq!(format!("{:.10e}", i8::MIN), "-1.2800000000e2");
136 assert_eq!(format!("{:.10e}", i16::MIN), "-3.2768000000e4");
137 assert_eq!(format!("{:.10e}", i32::MIN), "-2.1474836480e9");
138 assert_eq!(format!("{:.20e}", i64::MIN), "-9.22337203685477580800e18");
139 assert_eq!(format!("{:.40e}", i128::MIN), "-1.7014118346046923173168730371588410572800e38");
140
141 //test rounding
142 assert_eq!(format!("{:.1e}", i8::MIN), "-1.3e2");
143 assert_eq!(format!("{:.1e}", i16::MIN), "-3.3e4");
144 assert_eq!(format!("{:.1e}", i32::MIN), "-2.1e9");
145 assert_eq!(format!("{:.1e}", i64::MIN), "-9.2e18");
146 assert_eq!(format!("{:.1e}", i128::MIN), "-1.7e38");
147
148 //test huge precision
149 assert_eq!(format!("{:.1000e}", 1), format!("1.{}e0", "0".repeat(1000)));
150 //test zero precision
151 assert_eq!(format!("{:.0e}", 1), format!("1e0",));
152
153 //test padding with precision (and sign)
154 assert_eq!(format!("{:+10.3e}", 1), " +1.000e0");
79} 155}
80 156
81#[test] 157#[test]
@@ -86,6 +162,8 @@ fn test_format_int_zero() {
86 assert_eq!(format!("{:o}", 0), "0"); 162 assert_eq!(format!("{:o}", 0), "0");
87 assert_eq!(format!("{:x}", 0), "0"); 163 assert_eq!(format!("{:x}", 0), "0");
88 assert_eq!(format!("{:X}", 0), "0"); 164 assert_eq!(format!("{:X}", 0), "0");
165 assert_eq!(format!("{:e}", 0), "0e0");
166 assert_eq!(format!("{:E}", 0), "0E0");
89 167
90 assert_eq!(format!("{}", 0u32), "0"); 168 assert_eq!(format!("{}", 0u32), "0");
91 assert_eq!(format!("{:?}", 0u32), "0"); 169 assert_eq!(format!("{:?}", 0u32), "0");
@@ -93,6 +171,8 @@ fn test_format_int_zero() {
93 assert_eq!(format!("{:o}", 0u32), "0"); 171 assert_eq!(format!("{:o}", 0u32), "0");
94 assert_eq!(format!("{:x}", 0u32), "0"); 172 assert_eq!(format!("{:x}", 0u32), "0");
95 assert_eq!(format!("{:X}", 0u32), "0"); 173 assert_eq!(format!("{:X}", 0u32), "0");
174 assert_eq!(format!("{:e}", 0u32), "0e0");
175 assert_eq!(format!("{:E}", 0u32), "0E0");
96} 176}
97 177
98#[test] 178#[test]