@@ -29,6 +29,111 @@ use crate::values::{
29
29
StarlarkValue , Value , ValueError ,
30
30
} ;
31
31
32
+ const WRITE_PRECISION : usize = 6 ;
33
+
34
+ fn write_non_finite < W : fmt:: Write > ( output : & mut W , f : f64 ) -> fmt:: Result {
35
+ debug_assert ! ( f. is_nan( ) || f. is_infinite( ) ) ;
36
+ if f. is_nan ( ) {
37
+ write ! ( output, "nan" )
38
+ } else {
39
+ write ! (
40
+ output,
41
+ "{}inf" ,
42
+ if f. is_sign_positive( ) { "+" } else { "-" }
43
+ )
44
+ }
45
+ }
46
+
47
+ pub fn write_decimal < W : fmt:: Write > ( output : & mut W , f : f64 ) -> fmt:: Result {
48
+ if !f. is_finite ( ) {
49
+ write_non_finite ( output, f)
50
+ } else {
51
+ write ! ( output, "{:.prec$}" , f, prec = WRITE_PRECISION )
52
+ }
53
+ }
54
+
55
+ pub fn write_scientific < W : fmt:: Write > (
56
+ output : & mut W ,
57
+ f : f64 ,
58
+ exponent_char : char ,
59
+ strip_trailing_zeros : bool ,
60
+ ) -> fmt:: Result {
61
+ if !f. is_finite ( ) {
62
+ write_non_finite ( output, f)
63
+ } else {
64
+ let abs = f. abs ( ) ;
65
+ let exponent = if f == 0.0 {
66
+ 0
67
+ } else {
68
+ abs. log10 ( ) . floor ( ) as i32
69
+ } ;
70
+ let normal = if f == 0.0 {
71
+ 0.0
72
+ } else {
73
+ abs / 10f64 . powf ( exponent as f64 )
74
+ } ;
75
+
76
+ // start with "-" for a negative number
77
+ if f. is_sign_negative ( ) {
78
+ output. write_char ( '-' ) ?
79
+ }
80
+
81
+ // use the whole integral part of normal (a single digit)
82
+ output. write_fmt ( format_args ! ( "{}" , normal. trunc( ) ) ) ?;
83
+
84
+ // calculate the fractional tail for given precision
85
+ let mut tail = ( normal. fract ( ) * 10f64 . powf ( WRITE_PRECISION as f64 ) ) . round ( ) as u64 ;
86
+ let mut rev_tail = [ 0u8 ; WRITE_PRECISION ] ;
87
+ let mut rev_tail_len = 0 ;
88
+ let mut removing_trailing_zeros = strip_trailing_zeros;
89
+ for _ in 0 ..WRITE_PRECISION {
90
+ let tail_digit = tail % 10 ;
91
+ if tail_digit != 0 || !removing_trailing_zeros {
92
+ removing_trailing_zeros = false ;
93
+ rev_tail[ rev_tail_len] = tail_digit as u8 ;
94
+ rev_tail_len += 1 ;
95
+ }
96
+ tail /= 10 ;
97
+ }
98
+
99
+ // write fractional part
100
+ if rev_tail_len != 0 {
101
+ output. write_char ( '.' ) ?;
102
+ }
103
+ for digit in rev_tail[ 0 ..rev_tail_len] . iter ( ) . rev ( ) {
104
+ output. write_char ( ( b'0' + digit) as char ) ?;
105
+ }
106
+
107
+ // add exponent part
108
+ output. write_char ( exponent_char) ?;
109
+ output. write_fmt ( format_args ! ( "{:+03}" , exponent) )
110
+ }
111
+ }
112
+
113
+ pub fn write_compact < W : fmt:: Write > ( output : & mut W , f : f64 , exponent_char : char ) -> fmt:: Result {
114
+ if !f. is_finite ( ) {
115
+ write_non_finite ( output, f)
116
+ } else {
117
+ let abs = f. abs ( ) ;
118
+ let exponent = if f == 0.0 {
119
+ 0
120
+ } else {
121
+ abs. log10 ( ) . floor ( ) as i32
122
+ } ;
123
+
124
+ if exponent. abs ( ) >= WRITE_PRECISION as i32 {
125
+ // use scientific notation if exponent is outside of our precision (but strip 0s)
126
+ write_scientific ( output, f, exponent_char, true )
127
+ } else if f. fract ( ) == 0.0 {
128
+ // make sure there's a fractional part even if the number doesn't have it
129
+ output. write_fmt ( format_args ! ( "{:.1}" , f) )
130
+ } else {
131
+ // rely on the built-in formatting otherwise
132
+ output. write_fmt ( format_args ! ( "{}" , f) )
133
+ }
134
+ }
135
+ }
136
+
32
137
#[ derive( Clone , Dupe , Copy , Debug , AnyLifetime ) ]
33
138
pub struct StarlarkFloat ( pub f64 ) ;
34
139
@@ -70,19 +175,7 @@ where
70
175
71
176
impl Display for StarlarkFloat {
72
177
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
73
- if self . 0 . is_nan ( ) {
74
- write ! ( f, "nan" )
75
- } else if self . 0 . is_infinite ( ) {
76
- if self . 0 . is_sign_positive ( ) {
77
- write ! ( f, "+inf" )
78
- } else {
79
- write ! ( f, "-inf" )
80
- }
81
- } else if self . 0 . fract ( ) == 0.0 {
82
- write ! ( f, "{:.1}" , self . 0 )
83
- } else {
84
- write ! ( f, "{}" , self . 0 )
85
- }
178
+ write_compact ( f, self . 0 , 'e' )
86
179
}
87
180
}
88
181
@@ -208,8 +301,86 @@ impl<'v> StarlarkValue<'v> for StarlarkFloat {
208
301
209
302
#[ cfg( test) ]
210
303
mod tests {
304
+ use super :: * ;
211
305
use crate :: assert;
212
306
307
+ fn non_finite ( f : f64 ) -> String {
308
+ let mut buf = String :: new ( ) ;
309
+ write_non_finite ( & mut buf, f) . unwrap ( ) ;
310
+ buf
311
+ }
312
+
313
+ #[ test]
314
+ fn test_write_non_finite ( ) {
315
+ assert_eq ! ( non_finite( f64 :: NAN ) , "nan" ) ;
316
+ assert_eq ! ( non_finite( f64 :: INFINITY ) , "+inf" ) ;
317
+ assert_eq ! ( non_finite( f64 :: NEG_INFINITY ) , "-inf" ) ;
318
+ }
319
+
320
+ #[ test]
321
+ #[ should_panic]
322
+ fn test_write_non_finite_only_for_non_finite ( ) {
323
+ non_finite ( 0f64 ) ;
324
+ }
325
+
326
+ fn decimal ( f : f64 ) -> String {
327
+ let mut buf = String :: new ( ) ;
328
+ write_decimal ( & mut buf, f) . unwrap ( ) ;
329
+ buf
330
+ }
331
+
332
+ #[ test]
333
+ fn test_write_decimal ( ) {
334
+ assert_eq ! ( decimal( f64 :: NAN ) , "nan" ) ;
335
+ assert_eq ! ( decimal( f64 :: INFINITY ) , "+inf" ) ;
336
+ assert_eq ! ( decimal( f64 :: NEG_INFINITY ) , "-inf" ) ;
337
+
338
+ assert_eq ! ( decimal( 0f64 ) , "0.000000" ) ;
339
+ assert_eq ! ( decimal( std:: f64 :: consts:: PI ) , "3.141593" ) ;
340
+ assert_eq ! ( decimal( -std:: f64 :: consts:: E ) , "-2.718282" ) ;
341
+ assert_eq ! ( decimal( 1e10 ) , "10000000000.000000" ) ;
342
+ }
343
+
344
+ fn scientific ( f : f64 ) -> String {
345
+ let mut buf = String :: new ( ) ;
346
+ write_scientific ( & mut buf, f, 'e' , false ) . unwrap ( ) ;
347
+ buf
348
+ }
349
+
350
+ #[ test]
351
+ fn test_write_scientific ( ) {
352
+ assert_eq ! ( scientific( f64 :: NAN ) , "nan" ) ;
353
+ assert_eq ! ( scientific( f64 :: INFINITY ) , "+inf" ) ;
354
+ assert_eq ! ( scientific( f64 :: NEG_INFINITY ) , "-inf" ) ;
355
+
356
+ assert_eq ! ( scientific( 0f64 ) , "0.000000e+00" ) ;
357
+ assert_eq ! ( scientific( -0f64 ) , "-0.000000e+00" ) ;
358
+ assert_eq ! ( scientific( 1.23e45 ) , "1.230000e+45" ) ;
359
+ assert_eq ! ( scientific( -3.14e-145 ) , "-3.140000e-145" ) ;
360
+ assert_eq ! ( scientific( 1e300 ) , "1.000000e+300" ) ;
361
+ }
362
+
363
+ fn compact ( f : f64 ) -> String {
364
+ let mut buf = String :: new ( ) ;
365
+ write_compact ( & mut buf, f, 'e' ) . unwrap ( ) ;
366
+ buf
367
+ }
368
+
369
+ #[ test]
370
+ fn test_write_compact ( ) {
371
+ assert_eq ! ( compact( f64 :: NAN ) , "nan" ) ;
372
+ assert_eq ! ( compact( f64 :: INFINITY ) , "+inf" ) ;
373
+ assert_eq ! ( compact( f64 :: NEG_INFINITY ) , "-inf" ) ;
374
+
375
+ assert_eq ! ( compact( 0f64 ) , "0.0" ) ;
376
+ assert_eq ! ( compact( std:: f64 :: consts:: PI ) , "3.141592653589793" ) ;
377
+ assert_eq ! ( compact( -std:: f64 :: consts:: E ) , "-2.718281828459045" ) ;
378
+ assert_eq ! ( compact( 1e10 ) , "1e+10" ) ;
379
+ assert_eq ! ( compact( 1.23e45 ) , "1.23e+45" ) ;
380
+ assert_eq ! ( compact( -3.14e-145 ) , "-3.14e-145" ) ;
381
+ assert_eq ! ( compact( 1e300 ) , "1e+300" ) ;
382
+ }
383
+
213
384
#[ test]
214
385
fn test_arithmetic_operators ( ) {
215
386
assert:: all_true (
0 commit comments