1010//! available online: <https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html>.
1111
1212use crate :: num:: dec2flt:: common:: { ByteSlice , is_8digits} ;
13+ use crate :: kani;
1314
1415/// A decimal floating-point number, represented as a sequence of decimal digits.
1516#[ derive( Clone , Debug , PartialEq ) ]
@@ -83,6 +84,7 @@ impl DecimalSeq {
8384 //
8485 // Trim is only called in `right_shift` and `left_shift`.
8586 debug_assert ! ( self . num_digits <= Self :: MAX_DIGITS ) ;
87+ #[ kani:: loop_invariant( self . num_digits <= Self :: MAX_DIGITS ) ]
8688 while self . num_digits != 0 && self . digits [ self . num_digits - 1 ] == 0 {
8789 self . num_digits -= 1 ;
8890 }
@@ -98,6 +100,7 @@ impl DecimalSeq {
98100 let dp = self . decimal_point as usize ;
99101 let mut n = 0_u64 ;
100102
103+ #[ kani:: loop_invariant( n < 10u64 . pow( kaniindex as u32 ) ) ]
101104 for i in 0 ..dp {
102105 n *= 10 ;
103106 if i < self . num_digits {
@@ -129,7 +132,14 @@ impl DecimalSeq {
129132 let mut read_index = self . num_digits ;
130133 let mut write_index = self . num_digits + num_new_digits;
131134 let mut n = 0_u64 ;
132-
135+
136+ #[ kani:: loop_invariant( read_index <= Self :: MAX_DIGITS &&
137+ write_index == read_index + num_new_digits &&
138+ n < 10u64 << ( shift - 1 ) &&
139+ self . num_digits <= Self :: MAX_DIGITS &&
140+ self . decimal_point <= self . num_digits as i32 &&
141+ kani:: forall!( |i in ( 0 , DecimalSeq :: MAX_DIGITS ) | self . digits[ i] <= 9 )
142+ ) ]
133143 while read_index != 0 {
134144 read_index -= 1 ;
135145 write_index -= 1 ;
@@ -144,6 +154,7 @@ impl DecimalSeq {
144154 n = quotient;
145155 }
146156
157+ #[ kani:: loop_invariant( self . num_digits <= Self :: MAX_DIGITS && self . decimal_point <= self . num_digits as i32 ) ]
147158 while n > 0 {
148159 write_index -= 1 ;
149160 let quotient = n / 10 ;
@@ -171,13 +182,16 @@ impl DecimalSeq {
171182 let mut read_index = 0 ;
172183 let mut write_index = 0 ;
173184 let mut n = 0_u64 ;
185+ #[ kani:: loop_invariant( n == 0 || ( read_index > 0 && read_index <= self . num_digits + 64 - n. leading_zeros( ) as usize ) ) ]
174186 while ( n >> shift) == 0 {
175187 if read_index < self . num_digits {
176188 n = ( 10 * n) + self . digits [ read_index] as u64 ;
177189 read_index += 1 ;
178190 } else if n == 0 {
179191 return ;
180192 } else {
193+ kani:: assume ( read_index == self . num_digits ) ;
194+ #[ kani:: loop_invariant( n > 0 && read_index <= self . num_digits + 64 - n. leading_zeros( ) as usize && read_index > 0 ) ]
181195 while ( n >> shift) == 0 {
182196 n *= 10 ;
183197 read_index += 1 ;
@@ -194,13 +208,18 @@ impl DecimalSeq {
194208 return ;
195209 }
196210 let mask = ( 1_u64 << shift) - 1 ;
211+ #[ kani:: loop_invariant( self . num_digits <= Self :: MAX_DIGITS &&
212+ write_index < read_index &&
213+ write_index < Self :: MAX_DIGITS - self . num_digits. saturating_sub( read_index)
214+ ) ]
197215 while read_index < self . num_digits {
198216 let new_digit = ( n >> shift) as u8 ;
199217 n = ( 10 * ( n & mask) ) + self . digits [ read_index] as u64 ;
200218 read_index += 1 ;
201219 self . digits [ write_index] = new_digit;
202220 write_index += 1 ;
203221 }
222+ #[ kani:: loop_invariant( write_index <= Self :: MAX_DIGITS ) ]
204223 while n > 0 {
205224 let new_digit = ( n >> shift) as u8 ;
206225 n = 10 * ( n & mask) ;
@@ -363,6 +382,7 @@ fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usiz
363382 let pow5_b = ( 0x7FF & x_b) as usize ;
364383 let pow5 = & TABLE_POW5 [ pow5_a..] ;
365384
385+ #[ kani:: loop_invariant( true ) ]
366386 for ( i, & p5) in pow5. iter ( ) . enumerate ( ) . take ( pow5_b - pow5_a) {
367387 if i >= d. num_digits {
368388 return num_new_digits - 1 ;
@@ -377,3 +397,53 @@ fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usiz
377397
378398 num_new_digits
379399}
400+
401+ #[ cfg( kani) ]
402+ #[ unstable( feature = "kani" , issue = "none" ) ]
403+ pub mod decimal_seq_verify {
404+ use super :: * ;
405+
406+ impl kani:: Arbitrary for DecimalSeq {
407+ fn any ( ) -> DecimalSeq {
408+ let mut ret = DecimalSeq {
409+ num_digits : kani:: any ( ) ,
410+ decimal_point : kani:: any ( ) ,
411+ truncated : kani:: any ( ) ,
412+ digits : kani:: any ( ) } ;
413+ kani:: assume ( a. num_digits <= DecimalSeq :: MAX_DIGITS ) ;
414+ kani:: assume ( a. decimal_point >=0 ) ;
415+ kani:: assume ( a. decimal_point <= a. num_digits as i32 ) ;
416+ kani:: assume ( kani:: forall!( |i in ( 0 , DecimalSeq :: MAX_DIGITS ) | a. digits[ i] <= 9 ) ) ;
417+ ret
418+ }
419+ }
420+
421+ #[ kani:: proof]
422+ fn check_round ( ) {
423+ let mut a: DecimalSeq = kani:: any ( ) ;
424+ a. round ( ) ;
425+ }
426+
427+ #[ kani:: proof]
428+ fn check_number_of_digits_decimal_left_shift ( ) {
429+ let mut a: DecimalSeq = kani:: any ( ) ;
430+ let shift: usize = kani:: any_where ( |x| * x > 0 && * x <= 60 ) ;
431+ let n = number_of_digits_decimal_left_shift ( & a, shift) ;
432+ assert ! ( n <= 19 ) ;
433+ assert ! ( n == 19 || 1u64 << shift < 10u64 . pow( n as u32 + 1 ) )
434+ }
435+
436+ #[ kani:: proof]
437+ fn check_right_shift ( ) {
438+ let mut a: DecimalSeq = kani:: any ( ) ;
439+ let shift: usize = kani:: any_where ( |x| * x > 0 && * x <= 60 ) ;
440+ a. right_shift ( shift) ;
441+ }
442+
443+ #[ kani:: proof]
444+ fn check_left_shift ( ) {
445+ let mut a: DecimalSeq = kani:: any ( ) ;
446+ let shift: usize = kani:: any_where ( |x| * x > 0 && * x <= 60 ) ;
447+ a. left_shift ( shift) ;
448+ }
449+ }
0 commit comments