NSDecimalRound向下舍入Decimal值出现非预期结果的原因咨询
问题根因
这个异常和NSDecimalRound的舍入逻辑没有关系,问题出在你初始化Decimal的方式上。
- 你代码里写的
2341.2142、2341.2146这类小数字面量,会先被编译器解析为Double类型的二进制浮点数。而二进制浮点数本身就无法精确表示绝大多数十进制小数,实际存储的是和目标值有微小偏差的近似值:- 字面量
2341.2142对应的Double实际值约为2341.2141999999998,比真实的2341.2142略小 - 字面量
2341.2146对应的Double实际值约为2341.2145999999997,同样比真实值略小
- 字面量
- 当你把这个已经带误差的Double值赋值给
Decimal变量时,Decimal会精确存储这个有偏差的数值。此时调用NSDecimalRound选择.down(向下舍入,即向负无穷方向取不大于原值的最大符合精度值),自然会得到比你预期小0.0001的结果。 - 第一个例子里
2341.2143看起来结果正常,只是因为这个值对应的Double近似值刚好略大于真实值,向下舍入4位时刚好和你的预期一致,完全是巧合。
正确写法
要避免这个问题,核心是不要经过Double类型中转来初始化Decimal,从根源上切断二进制浮点数的精度误差传递:
- 优先使用字符串初始化
Decimal,可以保证十进制值完全精确:
var unrounded: Decimal = Decimal(string: "2341.2142")! var rounded = Decimal() NSDecimalRound(&rounded, &unrounded, 4, .down) print(rounded) // 输出2341.2142,符合预期
- 如果需要做数值计算,全程使用
Decimal的运算方法,不要先用Double完成计算再转成Decimal做舍入。
只要你使用
var x: Decimal = 2341.2142这种直接赋值浮点数字面量的写法,编译器就会自动先完成Double类型的解析,精度误差就有可能被引入,和后续的舍入操作无关。
内容的提问来源于stack exchange,提问作者Paul Patterson
相关产品推荐
相关产品推荐

