使用Int(round(x))是否安全?

时间:2017-05-11 07:03:44

标签: casting floating-point int rounding

假设您有一个double值,并希望将其舍入为整数...

许多round()函数返回一个double而不是一个整数:

  • C# - round(double) - >双
  • C++ - round(double) - >双
  • Darwin - round(double) - >双
  • Swift - double.rounded() - >双
  • Java - round(double) - > INT
  • Ruby:float.round() - > INT

(这很可能是因为doubles have a much wider range of possible values。)

鉴于此"默认"行为,它可能解释了为什么你会经常看到以下建议:

Int(round(myDouble))

(我们假设Int() removes everything after the decimal:4.9 - > 4。)

到目前为止一直很好,直到你意识到how complex floating points really are。例如。例如,55实际上可能会存储为54.9999999999999999

因此,听起来可能会发生以下情况:

Int(round(55.4))       // we ask it to round 55.4, expecting 55
Int(54.9999999999999)  // it rounded it to "55.0"
54                     // the Int() function removed all remaining digits

我们希望55.4四舍五入为55,但最终评估为54

  1. 如果我们使用Int(round(x))
  2. ,可能会发生上述情况
  3. 如果是这样,我们应该使用什么而不是Int(round())
  4. 相关:许多语言定义floor(double) -> doubleInt(floor(double))安全吗?

1 个答案:

答案 0 :(得分:2)

浮点模型是在这些基础上构建的:

  • 基地b
  • 该基数中有限位数的有效数字(p精度)
  • 用于移动浮点的指数e,也限制在某个范围内
  • 一个标志

所以浮点值是这样的:(-1)^signBit * significand * b^e

有效数可以用标准化形式x.xxxxxxxx表示,浮点数左边有1个非空数字(零除外,或最终值接近零,失去精度并逐渐下溢),p-1浮点后的数字。

但是通过适当地移动指数(e+1-p),它也可以被视为具有p位数xxxxxxxxx.0的整数。

对于指数的可合理范围,我们看到每个直到b^p的整数都可以由这样的浮点模型精确表示。由于精度有限,只有基数b中的最后一位数字会丢失,所以如果我们有一个太大而不适合有效数字的整数,它必然会有一个零分数部分。因此,除了一个整数值(零分数部分)之外,没有理由进行回答。

您注意到的唯一不安全部分是Int范围可能远小于浮点值范围。因此,将大浮点数转换为Int可能会导致溢出异常,或者更糟糕的是,带有未定义行为的静默溢出...

因此,为了消除分数部分,不需要转换为Int。它必须用于其他目的(比如提供只接受Int的程序的另一部分)。