如何用集合形式精确表示Java中double/float的数值范围?
没问题,咱们来把Java里的float和double的数值范围用严谨的数学集合形式定义出来——毕竟它们不像int那样是简单的整数区间,而是遵循IEEE 754标准的浮点数,得把正常规格化数、非规格化数(亚正规数)、特殊值都考虑进去才够精确。
1. Java float的精确集合定义
Java的float对应IEEE 754单精度32位浮点数,它的数值集合可以拆分为三个互不重叠的子集:正常规格化数、非规格化数、特殊值:
FLOAT = S_NORMAL ∪ S_SUBNORMAL ∪ S_SPECIAL
正常规格化数(S_NORMAL)
这是我们日常使用最多的浮点数部分,指数位既不全0也不全1:
S_NORMAL = { (-1)^s × (1 + m/2²³) × 2^(e-127) | s∈{0,1}, e∈[1,254], m∈[0,2²³-1] }
s是符号位:0代表正数,1代表负数e是8位指数位,取值范围1到254(避开全0和全1的特殊情况)m是23位尾数位,取值0到2²³-1
对应的数值范围是:-3.4028235×10³⁸到-1.1754944×10⁻³⁸,以及1.1754944×10⁻³⁸到3.4028235×10³⁸
非规格化数(S_SUBNORMAL)
这类数用来表示非常接近0的数值,填补规格化数和0之间的空隙,指数位全为0,尾数位不全为0:
S_SUBNORMAL = { (-1)^s × (m/2²³) × 2^(-126) | s∈{0,1}, m∈[1,2²³-1] }
对应的数值范围是:-1.17549435×10⁻³⁸ 到 -1.4012985×10⁻⁴⁵,以及 1.4012985×10⁻⁴⁵ 到 1.17549435×10⁻³⁸
另外,当尾数位全为0时,就是正0和负0(+0.0f和-0.0f),这两个在Java里是不同的二进制表示,但数学上通常视为同一个0。
特殊值(S_SPECIAL)
当指数位全为1时,就代表特殊值:
S_SPECIAL = { +Infinity, -Infinity, NaN }
+Infinity和-Infinity表示正无穷和负无穷NaN(非数值)有多种二进制表示,但Java里统一视为同一个逻辑值,用来表示无效运算的结果(比如0除以0)
2. Java double的精确集合定义
Java的double对应IEEE 754双精度64位浮点数,结构和float类似,只是位数更多(1位符号位、11位指数位、52位尾数位),同样拆分为三个子集:
DOUBLE = D_NORMAL ∪ D_SUBNORMAL ∪ D_SPECIAL
正常规格化数(D_NORMAL)
D_NORMAL = { (-1)^s × (1 + m/2⁵²) × 2^(e-1023) | s∈{0,1}, e∈[1,2046], m∈[0,2⁵²-1] }
e是11位指数位,取值1到2046m是52位尾数位,取值0到2⁵²-1
对应的数值范围是:-1.7976931348623157×10³⁰⁸到-2.2250738585072014×10⁻³⁰⁸,以及2.2250738585072014×10⁻³⁰⁸到1.7976931348623157×10³⁰⁸
非规格化数(D_SUBNORMAL)
D_SUBNORMAL = { (-1)^s × (m/2⁵²) × 2^(-1022) | s∈{0,1}, m∈[1,2⁵²-1] }
对应的数值范围是:-2.2250738585072014×10⁻³⁰⁸ 到 -4.940656458412465×10⁻³²⁴,以及 4.940656458412465×10⁻³²⁴ 到 2.2250738585072014×10⁻³⁰⁸
同样包含+0.0d和-0.0d的表示。
特殊值(D_SPECIAL)
D_SPECIAL = { +Infinity, -Infinity, NaN }
和float的特殊值逻辑完全一致。
简化版:仅有限非NaN数值
如果只需要考虑有限的、非NaN的数值(也就是日常业务中常用的范围,排除无穷和NaN),可以简化为:
FLOAT_FINITE = S_NORMAL ∪ S_SUBNORMAL ∪ {+0.0f, -0.0f}
DOUBLE_FINITE = D_NORMAL ∪ D_SUBNORMAL ∪ {+0.0d, -0.0d}
内容的提问来源于stack exchange,提问作者osanger




