【问题标题】:Objective C - divide long by a float without losing precisionObjective C - long 除以浮点数而不会丢失精度
【发布时间】:2017-07-31 15:10:05
【问题描述】:

我在将 long 类型与 float 相除时遇到了精度问题,最终弄乱了应用程序中的计算。假设我只是将毫秒转换为秒,因为这是我们的要求。下面是一个示例测试用例

long startTimeMS = 2529095;
NSTimeInterval expectedStartTime = 2529.095
NSTimeInterval startTime = startTimeMS / 1000.f; //Results to "2529.09497"
double startTimeDouble = startTimeMS / 1000.0 // Results to "2529.0949999999998"

XTCAssert(startTime == expectedStartTime, @"Invalid start time")

将 long 除以 float 时,精度会损失千分之三。与 long 除以 double 类似。

我怎样才能让它与预期的开始时间相同?我猜四舍五入到最接近的十进制千分之一也不错,但我在这里找到的四舍五入方法也不像这样可靠

float roundToTwo(float num)
{
    return round(100 * num) / 100;
}

最有可能返回相同的值

【问题讨论】:

  • 你确定你的例子吗? 2529095 / 100.f 导致 2595.09497 似乎是一个巨大的错误。
  • 您是否希望时代使用浮点数而不是整数? (您可以稍后将时间转换为双精度数以供显示)。
  • @MarkDickinson 我考虑回答在第一次计算中,longfloat 的转换失去了精度,但这并不能解释第二个结果。
  • @MarkDickinson 完全正确,评论说“结果为“2595.0949999999998””
  • @PascalCuoq:啊,对不起;我懂了。为自己是个白痴而道歉。原帖中肯定有错字吗?

标签: objective-c floating-point precision long-integer divide


【解决方案1】:

您误解了浮点的工作原理。浮点类型floatdouble二进制 类型。

您的小数 0.095 等于 9/100 + 5/1000 - 所有小数都是 1/(10^n) 小数的总和。

现在考虑普通分数,1/3 的十进制分数是多少?它是3/10 + 3/100 + 3/1000 + ... 或等效的0.333...没有确切的方法可以将其表示为相同的小数。

对于floatdouble 类型,小数部分是1/(2^n) 小数的总和。与上述类似,并非所有小数都可以精确地写成二进制小数。

除了使用不同的基数(十进制与二进制)引起的差异之外,计算机浮点在任何数字的位数上都有限制,而真正的数学是无限的,因此计算可能不会产生 精确 结果。

由于上述原因,最好的浮点练习永远不要测试完全相等,而是测试两个数字的差是否小于一个小值(这可能会根据数字的大小而变化)。在您的情况下,如果两个数字的差异小于 0.1 毫秒,您可能会认为它们是“相等的”。

现在已经在你给出的特定示例中说了所有这些,如果更正,数字实际上是相同的,但2529.095 也不是:

long startTimeMS = 2529095;
NSTimeInterval expectedStartTime = 2529.095;     // Result is approx "2529.0949999999998"
NSTimeInterval startTime = startTimeMS / 1000.0; // Result is approx "2529.0949999999998"

这两个数字比较相等,但并非所有值都如此。最好把你的断言写成:

XTCAssert(fabs(startTime - expectedStartTime) <= 0.0001, @"Invalid start time");

0.0001 应该是小到足以让您认为这两个值“相等”的任何几分之一秒。

如果您要进行需要精度的工作,您真的应该阅读浮点运算,因为您执行的操作越多,由于基数和有限数字造成的差异会累积,并且有一些技术可以最小化和处理这些问题。

HTH

【讨论】:

  • 将此作为使用 double 和 epsilon 大小写的正确答案,因为这是我能接受的最接近的答案
【解决方案2】:

您不应在此操作中使用浮点数 - 因为浮点数中以 10 为底的小数表示总是产生奇数。更好的解决方案是使用定点算法并将所有内容乘以 1000(如果您需要更多小数精度,则可以更多)。这避免了浮点计算中固有的舍入误差。

见:

https://softwareengineering.stackexchange.com/questions/101163/what-causes-floating-point-rounding-errors

为了更好地了解导致这些错误的原因。

【讨论】:

  • 我正在处理 NSTimeInterval,它是 double 类型。我需要将其转换为双精度/浮点数以在其中包含毫秒信息
  • 将数学作为定点(整数)数学,然后在最后转换为双精度。这将避免使用浮点数会产生舍入误差的累积。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-26
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 2012-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多