1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
//! 快速,准确地打印浮点数 [^1] 图 3 的几乎直接 (但略有优化) 的 Rust 翻译。
//!
//!
//! [^1]: Burger, R. G. and Dybvig, R. K. 1996. 打印浮点数
//! 快速准确。SIGPLAN 不是。31,5 (1996 年 5 月),108-116。
use crate::cmp::Ordering;
use crate::mem::MaybeUninit;
use crate::num::bignum::Big32x40 as Big;
use crate::num::bignum::Digit32 as Digit;
use crate::num::flt2dec::estimator::estimate_scaling_factor;
use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS};
static POW10: [Digit; 10] =
[1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000];
static TWOPOW10: [Digit; 10] =
[2, 20, 200, 2000, 20000, 200000, 2000000, 20000000, 200000000, 2000000000];
// 为 10^(2^n) 预先计算的 `Digit` 数组
static POW10TO16: [Digit; 2] = [0x6fc10000, 0x2386f2];
static POW10TO32: [Digit; 4] = [0, 0x85acef81, 0x2d6d415b, 0x4ee];
static POW10TO64: [Digit; 7] = [0, 0, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03];
static POW10TO128: [Digit; 14] = [
0, 0, 0, 0, 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da,
0xa6337f19, 0xe91f2603, 0x24e,
];
static POW10TO256: [Digit; 27] = [
0, 0, 0, 0, 0, 0, 0, 0, 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70,
0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 0x65f9ef17,
0x55bc28f2, 0x80dcc7f7, 0xf46eeddc, 0x5fdcefce, 0x553f7,
];
#[doc(hidden)]
pub fn mul_pow10(x: &mut Big, n: usize) -> &mut Big {
debug_assert!(n < 512);
if n & 7 != 0 {
x.mul_small(POW10[n & 7]);
}
if n & 8 != 0 {
x.mul_small(POW10[8]);
}
if n & 16 != 0 {
x.mul_digits(&POW10TO16);
}
if n & 32 != 0 {
x.mul_digits(&POW10TO32);
}
if n & 64 != 0 {
x.mul_digits(&POW10TO64);
}
if n & 128 != 0 {
x.mul_digits(&POW10TO128);
}
if n & 256 != 0 {
x.mul_digits(&POW10TO256);
}
x
}
fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big {
let largest = POW10.len() - 1;
while n > largest {
x.div_rem_small(POW10[largest]);
n -= largest;
}
x.div_rem_small(TWOPOW10[n]);
x
}
// 仅在 `x < 16 * scale` 时可用; `scaleN` 应该是 `scale.mul_small(N)`
fn div_rem_upto_16<'a>(
x: &'a mut Big,
scale: &Big,
scale2: &Big,
scale4: &Big,
scale8: &Big,
) -> (u8, &'a mut Big) {
let mut d = 0;
if *x >= *scale8 {
x.sub(scale8);
d += 8;
}
if *x >= *scale4 {
x.sub(scale4);
d += 4;
}
if *x >= *scale2 {
x.sub(scale2);
d += 2;
}
if *x >= *scale {
x.sub(scale);
d += 1;
}
debug_assert!(*x < *scale);
(d, x)
}
/// Dragon 的最短模式实现。
pub fn format_shortest<'a>(
d: &Decoded,
buf: &'a mut [MaybeUninit<u8>],
) -> (/*digits*/ &'a [u8], /*exp*/ i16) {
// 已知要格式化的数字 `v` 为:
// - 等于 `mant * 2^exp`;
// - 原始类型前面带有 `(mant - 2 *minus)* 2^exp`; and
// - 后面是原始类型的 `(mant + 2 *plus)* 2^exp`。
//
// 显然,`minus` 和 `plus` 不能为零。(对于无穷大,我们使用越界的值。) 我们还假定至少生成一个数字,即 `mant` 也不能为零。
//
// 这也意味着 `low = (mant - minus)*2^exp` 和 `high = (mant + plus)* 2^exp` 之间的任何数字都将 map 转换为该精确的浮点数,并包括原始尾数为偶数 (即 `!mant_was_odd`) 时的范围。
//
//
//
assert!(d.mant > 0);
assert!(d.minus > 0);
assert!(d.plus > 0);
assert!(d.mant.checked_add(d.plus).is_some());
assert!(d.mant.checked_sub(d.minus).is_some());
assert!(buf.len() >= MAX_SIG_DIGITS);
// `a.cmp(&b) < rounding` 是 `if d.inclusive {a <= b} else {a < b}`
let rounding = if d.inclusive { Ordering::Greater } else { Ordering::Equal };
// 从满足 `10^(k_0-1) < high <= 10^(k_0+1)` 的原始输入中估算 `k_0`。
// 满足 `10^(k-1) < high <= 10^k` 的紧定 `k` 稍后计算。
let mut k = estimate_scaling_factor(d.mant + d.plus, d.exp);
// 将 `{mant, plus, minus} * 2^exp` 转换为小数形式,以便:
// - `v = mant / scale`
// - `low = (mant - minus) / scale`
// - `high = (mant + plus) / scale`
let mut mant = Big::from_u64(d.mant);
let mut minus = Big::from_u64(d.minus);
let mut plus = Big::from_u64(d.plus);
let mut scale = Big::from_small(1);
if d.exp < 0 {
scale.mul_pow2(-d.exp as usize);
} else {
mant.mul_pow2(d.exp as usize);
minus.mul_pow2(d.exp as usize);
plus.mul_pow2(d.exp as usize);
}
// 将 `mant` 除以 `10^k`。现在是 `scale / 10 < mant + plus <= scale * 10`。
if k >= 0 {
mul_pow10(&mut scale, k as usize);
} else {
mul_pow10(&mut mant, -k as usize);
mul_pow10(&mut minus, -k as usize);
mul_pow10(&mut plus, -k as usize);
}
// `mant + plus > scale` (或 `>=`) 时修正。
// 我们实际上并没有修改 `scale`,因为我们可以跳过初始乘法。
// 现在 `scale < mant + plus <= scale * 10`,我们准备生成数字。
//
// 请注意,当 `scale - plus < mant < scale` 时,`d[0]`*可以* 为零。
// 在这种情况下,将立即触发舍入条件 (下面的 `up`)。
if scale.cmp(mant.clone().add(&plus)) < rounding {
// 相当于将 `scale` 缩放 10
k += 1;
} else {
mant.mul_small(10);
minus.mul_small(10);
plus.mul_small(10);
}
// 缓存 `(2, 4, 8) * scale` 用于数字生成。
let mut scale2 = scale.clone();
scale2.mul_pow2(1);
let mut scale4 = scale.clone();
scale4.mul_pow2(2);
let mut scale8 = scale.clone();
scale8.mul_pow2(3);
let mut down;
let mut up;
let mut i = 0;
loop {
// 不变量,其中 `d[0..n-1]` 是迄今为止生成的数字:
// - `v = mant / scale * 10^(k-n-1) + d[0..n-1] * 10^(k-n)`
// - `v - low = minus / scale * 10^(k-n-1)`
// - `high - v = plus / scale * 10^(k-n-1)`
// - `(mant + plus) / scale <= 10` (因此是 `mant / scale < 10`),其中 `d[i..j]` 是 `d [i] * 10^(ji) 的简写 + ...
// + d[j-1] * 10 + d[j]`.
// 生成一位数字: `d[n] = floor(mant / scale) < 10`。
let (d, _) = div_rem_upto_16(&mut mant, &scale, &scale2, &scale4, &scale8);
debug_assert!(d < 10);
buf[i] = MaybeUninit::new(b'0' + d);
i += 1;
// 这是对修改后的 Dragon 算法的简化描述。
// 为了方便起见,省略了许多中间派生和完整性参数。
//
// 从修改的不变量开始,因为我们已经更新了 `n`:
// - `v = mant / scale * 10^(k-n) + d[0..n-1] * 10^(k-n)`
// - `v - low = minus / scale * 10^(k-n)`
// - `high - v = plus / scale * 10^(k-n)`
//
// 假定 `d[0..n-1]` 是 `low` 和 `high` 之间的最短表示形式,即 `d[0..n-1]` 满足以下两个条件,但 `d[0..n-2]` 不满足:
//
// - `low < d[0..n-1] * 10^(k-n) < high` (双射性:数字四舍五入到 `v`) ; and
// - `abs(v / 10^(k-n) - d[0..n-1]) <= 1/2` (最后一位是正确的)。
//
// 第二个条件简化为 `2 * mant <= scale`。
// 根据 `mant`、`low` 和 `high` 求解不变量会产生第一个条件的更简单版本: `-plus < mant < minus`。
// 自 `-plus < 0 <= mant` 以来,我们拥有正确的最短表示形式 `mant < minus` 和 `2 * mant <= scale`。
// (当原始尾数为偶数时,前者变为 `mant <= minus`。)
//
// 当第二个不保存 (`2 * mant > scale`) 时,我们需要增加最后一位。
// 这足以恢复该条件:我们已经知道数字生成可以保证 `0 <= v / 10^(k-n) - d[0..n-1] < 1`。
// 在这种情况下,第一个条件变为 `-plus < mant - scale < minus`。
// 自从 `mant < scale` 产生以来,我们有了 `scale < mant + plus`。
// (同样,当原始尾数为偶数时,它变为 `scale <= mant + plus`。)
//
// 简而言之:
// - `mant < minus` (或 `<=`) 时,停止并四舍五入 `down` (保持数字不变)。
// - `scale < mant + plus` (或 `<=`) 时,停止并四舍五入 `up` (增加最后一位)。
// - 否则继续生成。
//
//
//
down = mant.cmp(&minus) < rounding;
up = scale.cmp(mant.clone().add(&plus)) < rounding;
if down || up {
break;
} // 我们具有最短的表示,请继续进行四舍五入
// 恢复不变量。
// 这使得算法总是终止: `minus` 和 `plus` 总是增加,但是 `mant` 被裁剪为 `scale` 模,并且 `scale` 是固定的。
//
mant.mul_small(10);
minus.mul_small(10);
plus.mul_small(10);
}
// 当 i) 仅触发了舍入条件,或者 ii) 两个条件都被触发并且平局打破更倾向于舍入时,发生舍入。
//
//
if up && (!down || *mant.mul_pow2(1) >= scale) {
// 如果四舍五入改变了长度,则指数也应改变。
// 似乎很难满足这个条件 (可能是不可能的),但是我们在这里只是安全和一致的。
//
// SAFETY: 我们在上面初始化了该内存。
if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }) {
buf[i] = MaybeUninit::new(c);
i += 1;
k += 1;
}
}
// SAFETY: 我们在上面初始化了该内存。
(unsafe { MaybeUninit::slice_assume_init_ref(&buf[..i]) }, k)
}
/// Dragon 的确切和固定模式实现。
pub fn format_exact<'a>(
d: &Decoded,
buf: &'a mut [MaybeUninit<u8>],
limit: i16,
) -> (/*digits*/ &'a [u8], /*exp*/ i16) {
assert!(d.mant > 0);
assert!(d.minus > 0);
assert!(d.plus > 0);
assert!(d.mant.checked_add(d.plus).is_some());
assert!(d.mant.checked_sub(d.minus).is_some());
// 从满足 `10^(k_0-1) < v <= 10^(k_0+1)` 的原始输入中估算 `k_0`。
let mut k = estimate_scaling_factor(d.mant, d.exp);
// `v = mant / scale`.
let mut mant = Big::from_u64(d.mant);
let mut scale = Big::from_small(1);
if d.exp < 0 {
scale.mul_pow2(-d.exp as usize);
} else {
mant.mul_pow2(d.exp as usize);
}
// 将 `mant` 除以 `10^k`。现在 `scale / 10 < mant <= scale * 10`。
if k >= 0 {
mul_pow10(&mut scale, k as usize);
} else {
mul_pow10(&mut mant, -k as usize);
}
// `mant + plus >= scale` 时的修正,`plus / scale = 10^-buf.len() / 2` 时的修正。
// 为了保留固定大小的 bignum,我们实际上使用 `mant + floor(plus) >= scale`。
// 我们实际上并没有修改 `scale`,因为我们可以跳过初始乘法。
// 再次使用最短算法,`d[0]` 可以为零,但最终会四舍五入。
if *div_2pow10(&mut scale.clone(), buf.len()).add(&mant) >= scale {
// 相当于将 `scale` 缩放 10
k += 1;
} else {
mant.mul_small(10);
}
// 如果使用的是最后一位数字的限制,则需要在实际渲染之前缩短缓冲区,以避免双舍入。
//
// 请注意,在进行舍入操作时,我们必须再次扩大缓冲区!
let mut len = if k < limit {
// 糟糕,我们甚至无法产生 *一位* 数字。
// 例如,当我们有类似 9.5 的值并将其四舍五入时,这是可能的。
// 我们返回一个空缓冲区,但以后的向上舍入情况除外,后者发生在 `k == limit` 且必须产生一位数字的情况下。
//
0
} else if ((k as i32 - limit as i32) as usize) < buf.len() {
(k - limit) as usize
} else {
buf.len()
};
if len > 0 {
// 缓存 `(2, 4, 8) * scale` 用于数字生成。
// (这可能很昂贵,因此在缓冲区为空时不要计算它们。)
let mut scale2 = scale.clone();
scale2.mul_pow2(1);
let mut scale4 = scale.clone();
scale4.mul_pow2(2);
let mut scale8 = scale.clone();
scale8.mul_pow2(3);
for i in 0..len {
if mant.is_zero() {
// 以下数字全为零,我们在这里停止 *不要* 尝试进行舍入! 而是填写剩余的数字。
//
for c in &mut buf[i..len] {
*c = MaybeUninit::new(b'0');
}
// SAFETY: 我们在上面初始化了该内存。
return (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k);
}
let mut d = 0;
if mant >= scale8 {
mant.sub(&scale8);
d += 8;
}
if mant >= scale4 {
mant.sub(&scale4);
d += 4;
}
if mant >= scale2 {
mant.sub(&scale2);
d += 2;
}
if mant >= scale {
mant.sub(&scale);
d += 1;
}
debug_assert!(mant < scale);
debug_assert!(d < 10);
buf[i] = MaybeUninit::new(b'0' + d);
mant.mul_small(10);
}
}
// 如果以下几位恰好是 5000,则四舍五入,如果我们停在数字的中间,请检查前一位并尝试四舍五入为偶数 (即,避免在前一位为偶数时四舍五入)。
//
//
let order = mant.cmp(scale.mul_small(5));
if order == Ordering::Greater
|| (order == Ordering::Equal
// SAFETY: `buf[len-1]` 被初始化。
&& len > 0 && unsafe { buf[len - 1].assume_init() } & 1 == 1)
{
// 如果四舍五入改变了长度,则指数也应改变。
// 但是我们被要求固定位数,所以不要改变缓冲区...
// SAFETY: 我们在上面初始化了该内存。
if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) {
// ...除非我们被要求改为固定精度。
// 我们还需要检查一下,如果原始缓冲区为空,则只能在 `k == limit` (edge 情况) 下添加附加数字。
//
k += 1;
if k > limit && len < buf.len() {
buf[len] = MaybeUninit::new(c);
len += 1;
}
}
}
// SAFETY: 我们在上面初始化了该内存。
(unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k)
}