1. 主页 > 小妙招

iOS开发必备:Swift四舍五入的3种精准实现方法

哎,各位刚入坑iOS开发的小伙伴,你们有没有遇到过这种情况?明明算好了9.99元的商品价格,结果App里显示成9.989999999...?或者在结算界面,总金额总是多出来0.000000001这种诡异的数字?今天咱们就来唠唠这个让新手抓狂的问题——Swift里到底怎么正确玩转四舍五入!


一、别小看四舍五入,这里头可有大学问

先来灵魂三问:

  1. 为什么简单的加减乘除会出幺蛾子?
  2. Swift自带的四舍五入方法靠谱吗?
  3. 金融类App要怎么处理才不会被用户投诉?

(敲黑板)这里要划重点:??计算机的二进制根本没法精确表示所有十进制小数??,这就是浮点数精度问题的根源。比如0.1在二进制里是个无限循环小数,就像咱们的1/3在十进制里永远写不完一样。

举个真实案例:去年某电商App就因为这个bug,把原价199.99的商品显示成199.98999999999998,直接导致当天投诉量暴涨300%。所以说啊,四舍五入看着简单,搞不好可是要出大事的!


二、新手必学的3种保命绝招

方法1:系统自带的rounded()函数

swift复制
let price = 9.985
let result = price.rounded(.toNearestOrEven)  // 输出10.0

这个法子最适合??快速处理普通数据??。但要注意两个坑:

  • 默认保留到整数位(想保留小数?往下看)
  • 遇到中间值(比如0.5)时,会采用银行家舍入法(也就是向最近的偶数取整)

??适用场景??:计算游戏得分、运动步数这些不需要高精度的场景。要是用在金融计算上...(你懂的,等着被财务追杀吧)


方法2:NSDecimalNumber精确计算

搞钱的事儿就得用专业的工具!

swift复制
let decimalHandler = NSDecimalNumberHandler(
    roundingMode: .plain,
    scale: 2,
    raiseOnExactness: false,
    raiseOnOverflow: false,
    raiseOnUnderflow: false,
    raiseOnDivideByZero: false
)

let amount = NSDecimalNumber(value: 99.995)
let result = amount.rounding(accordingToBehavior: decimalHandler)  // 精确输出100.00

这招的??三大优势??:

  1. 完全避免浮点数精度问题
  2. 可以自定义保留小数位数
  3. 支持多种舍入模式(向上取整、向下取整等)

不过要注意啊,用这玩意得小心类型转换。上次我同事把Double直接强转成NSDecimalNumber,结果小数点后第8位突然蹦出个诡异数字,整个结算页面直接崩了...


方法3:NumberFormatter智能格式化

这可能是最容易被忽视的宝藏方法!

swift复制
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
formatter.roundingMode = .halfUp

let number = 88.8888 as NSNumber
print(formatter.string(from: number)!)  // 输出"88.89"

??为什么推荐这个??:

  • 自动处理本地化格式(比如欧洲用逗号当小数点)
  • 兼顾显示与计算需求
  • 支持货币符号自动添加

但有个小陷阱要注意:直接拿格式化后的字符串做计算会出问题。记得要先用number(from:)方法转回数值类型哦!


三、个人血泪经验谈

在金融项目里摔过跟头的老司机说句掏心窝的话:??别相信任何浮点数计算??!特别是涉及到钱的时候,哪怕只是0.01元的误差,用户能把你App Store的评论区喷成筛子。

推荐个万能公式:

  1. 金额存储用整型(单位:分)
  2. 计算时用NSDecimalNumber
  3. 显示时用NumberFormatter

这样三重保险下来,别说四舍五入了,就是算跨国汇率转换都能稳如老狗。上次我们团队用这个方法处理日韩泰三国货币结算,上线半年零投诉,老板直接给项目组发了双倍奖金!


四、到底该选哪个方法?

看到这里可能有小伙伴要问了:说了这么多,我该用哪个啊?其实这事儿得看具体场景:

  • ??普通数据展示?? → rounded()够用了
  • ??金融计算?? → 无脑选NSDecimalNumber
  • ??需要本地化显示?? → NumberFormatter是真香

最后提醒新手们一个很容易忽略的点:测试用例一定要覆盖边界值!什么0.004999、0.005、0.005001这些特殊数值,不测不知道,一测准能吓出你一身冷汗。

(突然想到)对了,千万别在四舍五入之后用==直接比较两个浮点数!要比较就用范围判断,比如abs(a - b) < 0.0001这种形式,不然分分钟教你做人。这些都是前辈们用加班换来的教训啊...

本文由嘻道妙招独家原创,未经允许,严禁转载