Yi Kong | ce81bb6 | 2020-08-31 01:21:33 +0800 | [diff] [blame^] | 1 | // Translated from C to Rust. The original C code can be found at |
| 2 | // https://github.com/ulfjack/ryu and carries the following license: |
| 3 | // |
| 4 | // Copyright 2018 Ulf Adams |
| 5 | // |
| 6 | // The contents of this file may be used under the terms of the Apache License, |
| 7 | // Version 2.0. |
| 8 | // |
| 9 | // (See accompanying file LICENSE-Apache or copy at |
| 10 | // http://www.apache.org/licenses/LICENSE-2.0) |
| 11 | // |
| 12 | // Alternatively, the contents of this file may be used under the terms of |
| 13 | // the Boost Software License, Version 1.0. |
| 14 | // (See accompanying file LICENSE-Boost or copy at |
| 15 | // https://www.boost.org/LICENSE_1_0.txt) |
| 16 | // |
| 17 | // Unless required by applicable law or agreed to in writing, this software |
| 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 19 | // KIND, either express or implied. |
| 20 | |
| 21 | use core::ptr; |
| 22 | |
| 23 | // Returns (lo, hi). |
| 24 | #[cfg(not(integer128))] |
| 25 | #[cfg_attr(feature = "no-panic", inline)] |
| 26 | pub fn umul128(a: u64, b: u64) -> (u64, u64) { |
| 27 | let a_lo = a as u32; |
| 28 | let a_hi = (a >> 32) as u32; |
| 29 | let b_lo = b as u32; |
| 30 | let b_hi = (b >> 32) as u32; |
| 31 | |
| 32 | let b00 = a_lo as u64 * b_lo as u64; |
| 33 | let b01 = a_lo as u64 * b_hi as u64; |
| 34 | let b10 = a_hi as u64 * b_lo as u64; |
| 35 | let b11 = a_hi as u64 * b_hi as u64; |
| 36 | |
| 37 | let b00_lo = b00 as u32; |
| 38 | let b00_hi = (b00 >> 32) as u32; |
| 39 | |
| 40 | let mid1 = b10 + b00_hi as u64; |
| 41 | let mid1_lo = mid1 as u32; |
| 42 | let mid1_hi = (mid1 >> 32) as u32; |
| 43 | |
| 44 | let mid2 = b01 + mid1_lo as u64; |
| 45 | let mid2_lo = mid2 as u32; |
| 46 | let mid2_hi = (mid2 >> 32) as u32; |
| 47 | |
| 48 | let p_hi = b11 + mid1_hi as u64 + mid2_hi as u64; |
| 49 | let p_lo = ((mid2_lo as u64) << 32) | b00_lo as u64; |
| 50 | |
| 51 | (p_lo, p_hi) |
| 52 | } |
| 53 | |
| 54 | #[cfg(not(integer128))] |
| 55 | #[cfg_attr(feature = "no-panic", inline)] |
| 56 | pub fn shiftright128(lo: u64, hi: u64, dist: u32) -> u64 { |
| 57 | // We don't need to handle the case dist >= 64 here (see above). |
| 58 | debug_assert!(dist > 0); |
| 59 | debug_assert!(dist < 64); |
| 60 | (hi << (64 - dist)) | (lo >> dist) |
| 61 | } |
| 62 | |
| 63 | #[cfg_attr(feature = "no-panic", inline)] |
| 64 | pub fn div5(x: u64) -> u64 { |
| 65 | x / 5 |
| 66 | } |
| 67 | |
| 68 | #[cfg_attr(feature = "no-panic", inline)] |
| 69 | pub fn div10(x: u64) -> u64 { |
| 70 | x / 10 |
| 71 | } |
| 72 | |
| 73 | #[cfg_attr(feature = "no-panic", inline)] |
| 74 | pub fn div100(x: u64) -> u64 { |
| 75 | x / 100 |
| 76 | } |
| 77 | |
| 78 | #[cfg_attr(feature = "no-panic", inline)] |
| 79 | fn pow5_factor(mut value: u64) -> u32 { |
| 80 | let mut count = 0u32; |
| 81 | loop { |
| 82 | debug_assert!(value != 0); |
| 83 | let q = div5(value); |
| 84 | let r = (value as u32).wrapping_sub(5u32.wrapping_mul(q as u32)); |
| 85 | if r != 0 { |
| 86 | break; |
| 87 | } |
| 88 | value = q; |
| 89 | count += 1; |
| 90 | } |
| 91 | count |
| 92 | } |
| 93 | |
| 94 | // Returns true if value is divisible by 5^p. |
| 95 | #[cfg_attr(feature = "no-panic", inline)] |
| 96 | pub fn multiple_of_power_of_5(value: u64, p: u32) -> bool { |
| 97 | // I tried a case distinction on p, but there was no performance difference. |
| 98 | pow5_factor(value) >= p |
| 99 | } |
| 100 | |
| 101 | // Returns true if value is divisible by 2^p. |
| 102 | #[cfg_attr(feature = "no-panic", inline)] |
| 103 | pub fn multiple_of_power_of_2(value: u64, p: u32) -> bool { |
| 104 | debug_assert!(value != 0); |
| 105 | debug_assert!(p < 64); |
| 106 | // __builtin_ctzll doesn't appear to be faster here. |
| 107 | (value & ((1u64 << p) - 1)) == 0 |
| 108 | } |
| 109 | |
| 110 | #[cfg(integer128)] |
| 111 | #[cfg_attr(feature = "no-panic", inline)] |
| 112 | pub fn mul_shift_64(m: u64, mul: &(u64, u64), j: u32) -> u64 { |
| 113 | let b0 = m as u128 * mul.0 as u128; |
| 114 | let b2 = m as u128 * mul.1 as u128; |
| 115 | (((b0 >> 64) + b2) >> (j - 64)) as u64 |
| 116 | } |
| 117 | |
| 118 | #[cfg(integer128)] |
| 119 | #[cfg_attr(feature = "no-panic", inline)] |
| 120 | pub unsafe fn mul_shift_all_64( |
| 121 | m: u64, |
| 122 | mul: &(u64, u64), |
| 123 | j: u32, |
| 124 | vp: *mut u64, |
| 125 | vm: *mut u64, |
| 126 | mm_shift: u32, |
| 127 | ) -> u64 { |
| 128 | ptr::write(vp, mul_shift_64(4 * m + 2, mul, j)); |
| 129 | ptr::write(vm, mul_shift_64(4 * m - 1 - mm_shift as u64, mul, j)); |
| 130 | mul_shift_64(4 * m, mul, j) |
| 131 | } |
| 132 | |
| 133 | #[cfg(not(integer128))] |
| 134 | #[cfg_attr(feature = "no-panic", inline)] |
| 135 | pub unsafe fn mul_shift_all_64( |
| 136 | mut m: u64, |
| 137 | mul: &(u64, u64), |
| 138 | j: u32, |
| 139 | vp: *mut u64, |
| 140 | vm: *mut u64, |
| 141 | mm_shift: u32, |
| 142 | ) -> u64 { |
| 143 | m <<= 1; |
| 144 | // m is maximum 55 bits |
| 145 | let (lo, tmp) = umul128(m, mul.0); |
| 146 | let (mut mid, mut hi) = umul128(m, mul.1); |
| 147 | mid = mid.wrapping_add(tmp); |
| 148 | hi = hi.wrapping_add((mid < tmp) as u64); // overflow into hi |
| 149 | |
| 150 | let lo2 = lo.wrapping_add(mul.0); |
| 151 | let mid2 = mid.wrapping_add(mul.1).wrapping_add((lo2 < lo) as u64); |
| 152 | let hi2 = hi.wrapping_add((mid2 < mid) as u64); |
| 153 | ptr::write(vp, shiftright128(mid2, hi2, j - 64 - 1)); |
| 154 | |
| 155 | if mm_shift == 1 { |
| 156 | let lo3 = lo.wrapping_sub(mul.0); |
| 157 | let mid3 = mid.wrapping_sub(mul.1).wrapping_sub((lo3 > lo) as u64); |
| 158 | let hi3 = hi.wrapping_sub((mid3 > mid) as u64); |
| 159 | ptr::write(vm, shiftright128(mid3, hi3, j - 64 - 1)); |
| 160 | } else { |
| 161 | let lo3 = lo + lo; |
| 162 | let mid3 = mid.wrapping_add(mid).wrapping_add((lo3 < lo) as u64); |
| 163 | let hi3 = hi.wrapping_add(hi).wrapping_add((mid3 < mid) as u64); |
| 164 | let lo4 = lo3.wrapping_sub(mul.0); |
| 165 | let mid4 = mid3.wrapping_sub(mul.1).wrapping_sub((lo4 > lo3) as u64); |
| 166 | let hi4 = hi3.wrapping_sub((mid4 > mid3) as u64); |
| 167 | ptr::write(vm, shiftright128(mid4, hi4, j - 64)); |
| 168 | } |
| 169 | |
| 170 | shiftright128(mid, hi, j - 64 - 1) |
| 171 | } |