Swift中是否有内置方法获取CGFloat的尾数与指数(非字符串形式)
获取CGFloat的十进制尾数与指数
你提到的Double的significand和exponent属性是基于二进制浮点数表示的,和我们日常认知的十进制尾数(1≤m<10)、指数完全不是一回事,你的理解没错。
原实现的三个明显问题
- 性能低效:通过循环逐次乘/除10调整数值,极小或极大的CGFloat会导致循环次数过多,拖慢执行速度。
- 精度损失:浮点数的十进制运算存在天然精度误差,处理
0.1这类无法用二进制精确表示的数时,尾数会出现偏差。 - 边界情况缺失:完全忽略负数、0、NaN、无穷大等特殊值,遇到这些情况要么结果错误,要么陷入无限循环。
更可靠的实现方案
利用对数运算直接推导指数和尾数,同时覆盖所有边界场景:
extension CGFloat { var decimalDecomposition: (mantissa: CGFloat, exponent: Int)? { let value = self // 处理特殊值:0、NaN、无穷大 guard value != 0, value.isFinite else { return nil } let absoluteValue = abs(value) // 计算以10为底的对数,得到指数的浮点形式 let log10Value = log10(absoluteValue) // 取整数部分作为初始指数 var exponent = Int(log10Value) // 计算初始尾数 var mantissa = absoluteValue / pow(10, CGFloat(exponent)) // 调整尾数到[1,10)区间 if mantissa >= 10 { mantissa /= 10 exponent += 1 } else if mantissa < 1 { mantissa *= 10 exponent -= 1 } // 保留原数符号 if value < 0 { mantissa = -mantissa } return (mantissa, exponent) } }
测试示例
// 常规值测试 if let result = CGFloat(0.00123).decimalDecomposition { print("\(result.mantissa)E\(result.exponent)") // 输出:1.23E-3 } if let result = CGFloat(12).decimalDecomposition { print("\(result.mantissa)E\(result.exponent)") // 输出:1.2E1 } if let result = CGFloat(1000).decimalDecomposition { print("\(result.mantissa)E\(result.exponent)") // 输出:1.0E3 } // 负数测试 if let result = CGFloat(-0.0456).decimalDecomposition { print("\(result.mantissa)E\(result.exponent)") // 输出:-4.56E-2 }
关于指数返回类型的说明
十进制分解的指数必然是整数(代表10的整数次幂),返回Int类型更符合逻辑,也能避免不必要的浮点转换。如果后续场景需要浮点型,可自行转换为CGFloat或Double。
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

