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
//! 将十进制字符串转换为 IEEE 754 二进制浮点数。
//!
//! # 问题陈述
//!
//! 我们给了一个十进制字符串,例如 `12.34e56`。
//! 该字符串由整数 (`12`),小数 (`34`) 和指数 (`56`) 组成。所有部分都是可选的,缺少则解释为零。
//!
//! 我们寻求最接近十进制字符串确切值的 IEEE 754 浮点数。
//! 众所周知,许多十进制字符串在基数 2 中都没有终止表示,因此我们将 0.5 单位最后舍入 (换句话说,尽可能)。
//! 领带 (精确到两个连续浮点之间的中间的十进制值) 通过半对偶策略 (也称为银行家舍入) 来解决。
//!
//! 不用说,这在实现复杂性和所用的 CPU 周期方面都相当困难。
//!
//! # Implementation
//!
//! 首先,我们忽略迹象。或者更确切地说,我们在转换过程的开始就将其删除,然后在结束时将其重新应用。
//! 这在所有 edge 情况下都是正确的,因为 IEEE 浮点数对称于零左右,取反则仅翻转第一位。
//!
//! 然后,我们通过调整指数来删除小数点:从概念上讲,`12.34e56` 变为 `1234e54`,我们用正整数 `f = 1234` 和整数 `e = 54` 对其进行描述。
//! 在解析阶段之后,几乎所有代码都使用 `(f, e)` 表示形式。
//!
//! 然后,我们尝试使用机器大小的整数和固定大小的小浮点数 (首先是 `f32`/`f64`,然后是具有 64 位有效数的类型) 的一长串越来越普遍和昂贵的特殊情况。
//!
//! 扩展精度算法使用 Eisel-Lemire 算法,该算法使用 128 位 (或 192 位) 表示,可以准确快速地计算绝大多数浮点数。
//! 当所有这些都失败时,我们硬着头皮求助于使用大十进制表示,将数字移入范围,计算高位有效位并精确四舍五入到最近的表示。
//!
//! 需要注意的另一个方面是 ``RawFloat`` trait,几乎所有函数都通过 ``RawFloat`` trait 进行了参数化。有人可能认为解析为 `f64` 并将结果转换为 `f32` 就足够了。
//! 不幸的是,这不是我们生活的世界,这与使用基数二进位或半到四舍五入四舍五入无关。
//!
//! 例如,考虑两种类型的 `d2` 和 `d4`,它们代表具有两个十进制数字和四个十进制数字的十进制类型,并以 "0.01499" 作为输入。让我们使用向上舍入。
//! 直接转到两位十进制数字将得到 `0.01`,但是如果我们首先四舍五入到四位数字,则会得到 `0.0150`,然后将其四舍五入为 `0.02`。
//! 同样的原理也适用于其他操作,如果要获得 0.5 ULP 精度,则需要 *进行全精度的所有操作,并在末尾将* exactly once 舍入*,同时考虑所有截断的位。
//!
//! 首先,此模块及其子级实现以下算法:
//! "以每秒千兆字节的速度进行数字解析",在线提供: <https://arxiv.org/abs/2101.11408>。
//!
//! # Other
//!
//! 转换应 *从不* panic。
//! 在代码中有断言和显式的 panics,但是它们绝不应该被触发,而仅用作内部的健全性检查。任何 panics 都应视为错误。
//!
//! 虽然有单元测试,但它们在确保正确性上还远远不够,它们只覆盖了很小一部分可能的错误。
//! 更广泛的测试作为 Python 脚本位于目录 `src/etc/test-float-parse` 中。
//!
//! 关于整型溢出的注意事项:该文件的许多部分都使用十进制指数 `e` 来执行算术运算。
//! 首先,我们将小数点移动:在第一个十进制数字之前,在最后一个十进制数字之后,依此类推。如果不小心这样做可能会溢出。
//! 我们依靠解析子模块仅分发足够小的指数,其中 "sufficient" 表示 "such that the exponent +/- the number of decimal digits fits into a 64 bit integer"。
//! 较大的指数被接受,但是我们不对它们进行算术运算,它们立即变成 {positive,negative} {zero,infinity}。
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
#![doc(hidden)]
#![unstable(
feature = "dec2flt",
reason = "internal routines only exposed for testing",
issue = "none"
)]
use crate::error::Error;
use crate::fmt;
use crate::str::FromStr;
use self::common::BiasedFp;
use self::float::RawFloat;
use self::lemire::compute_float;
use self::parse::{parse_inf_nan, parse_number};
use self::slow::parse_long_mantissa;
mod common;
mod decimal;
mod fpu;
mod slow;
mod table;
// 在 flt2dec 中使用 float,在单元测试中全部使用。
pub mod float;
pub mod lemire;
pub mod number;
pub mod parse;
macro_rules! from_str_float_impl {
($t:ty) => {
#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for $t {
type Err = ParseFloatError;
/// 将以 10 为底的字符串转换为浮点数。
/// 接受可选的十进制指数。
///
/// 该函数接受诸如以下的字符串
///
/// * '3.14'
/// * '-3.14'
/// * '2.5E10',或等效的 '2.5e10'
/// * '2.5E-10'
/// * '5.'
/// * '.5',或等效地,'0.5'
/// * 'inf', '-inf', '+infinity', 'NaN'
///
/// 请注意,字母字符不区分大小写。
///
/// 前导和尾随空格表示错误。
///
/// # Grammar
///
/// 小写时遵循以下 [EBNF] 语法的所有字符串都将返回 [`Ok`]:
///
/// ```txt
/// Float ::= Sign? ( 'inf' | 'infinity' | 'nan' | Number )
/// Number ::= ( Digit+ |
/// Digit+ '.' Digit* |
/// Digit* '.' Digit+ ) Exp?
/// Exp ::= 'e' Sign? Digit+
/// Sign ::= [+-]
/// Digit ::= [0-9]
/// ```
///
/// [EBNF]: https://www.w3.org/TR/REC-xml/#sec-notation
///
/// # Arguments
///
/// * src - 字符串
///
/// # 返回值
///
/// `Err(ParseFloatError)` 如果字符串不代表有效数字。
/// 否则,`Ok(n)`,其中 `n` 是与 `src` 表示的数字最接近的可表示浮点数 (遵循与原始运算结果相同的舍入规则)。
///
///
///
///
// 我们添加 `#[inline(never)]` 属性,因为它的内容将填充 `dec2flt` 的内容,`dec2flt` 有 #[inline(always)]。
// 由于 `dec2flt` 是泛型,因此 `dec2flt` 没有属性的此函数上的正常内联属性会导致大量重复生成 `dec2flt`,尽管事实上最多只能存在 2 个可能的实例。
// 添加 #[inline(never)] 可以避免这种情况。
//
//
//
#[inline(never)]
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
dec2flt(src)
}
}
};
}
from_str_float_impl!(f32);
from_str_float_impl!(f64);
/// 解析浮点数时可以返回的错误。
///
/// 该错误用作 [`f32`] 和 [`f64`] 的 [`FromStr`] 实现的错误类型。
///
///
/// # Example
///
/// ```
/// use std::str::FromStr;
///
/// if let Err(e) = f64::from_str("a.12") {
/// println!("Failed conversion to f64: {e}");
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct ParseFloatError {
kind: FloatErrorKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum FloatErrorKind {
Empty,
Invalid,
}
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for ParseFloatError {
#[allow(deprecated)]
fn description(&self) -> &str {
match self.kind {
FloatErrorKind::Empty => "cannot parse float from empty string",
FloatErrorKind::Invalid => "invalid float literal",
}
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for ParseFloatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(deprecated)]
self.description().fmt(f)
}
}
#[inline]
pub(super) fn pfe_empty() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Empty }
}
// 用于单元测试,保持公开。
// 这比公开 FloatErrorKind 和 ParseFloatError::kind 好得多。
#[inline]
pub fn pfe_invalid() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Invalid }
}
/// 将 `BiasedFp` 转换为最接近的机器浮点类型。
fn biased_fp_to_float<T: RawFloat>(x: BiasedFp) -> T {
let mut word = x.f;
word |= (x.e as u64) << T::MANTISSA_EXPLICIT_BITS;
T::from_u64_bits(word)
}
/// 将十进制字符串转换为浮点数。
#[inline(always)] // 将被内联成一个带有 `#[inline(never)]` 的函数,见上
pub fn dec2flt<F: RawFloat>(s: &str) -> Result<F, ParseFloatError> {
let mut s = s.as_bytes();
let c = if let Some(&c) = s.first() {
c
} else {
return Err(pfe_empty());
};
let negative = c == b'-';
if c == b'-' || c == b'+' {
s = &s[1..];
}
if s.is_empty() {
return Err(pfe_invalid());
}
let mut num = match parse_number(s) {
Some(r) => r,
None if let Some(value) = parse_inf_nan(s, negative) => return Ok(value),
None => return Err(pfe_invalid()),
};
num.negative = negative;
if let Some(value) = num.try_fast_path::<F>() {
return Ok(value);
}
// 如果有效数字被截断,那么只有当 `mantissa + 1` 产生不同的结果时,我们才会有舍入错误。
// 如果 Eisel-Lemire 算法无法在第一次通过时正确取整,我们也会避免使用多余的 Eisel-Lemire 算法。
//
//
let mut fp = compute_float::<F>(num.exponent, num.mantissa);
if num.many_digits && fp.e >= 0 && fp != compute_float::<F>(num.exponent, num.mantissa + 1) {
fp.e = -1;
}
// 无法使用 Eisel-Lemire 算法正确舍入浮点数。
// 回退到较慢但始终正确的算法。
if fp.e < 0 {
fp = parse_long_mantissa::<F>(s);
}
let mut float = biased_fp_to_float::<F>(fp);
if num.negative {
float = -float;
}
Ok(float)
}