Objective C - 在不丢失精度的情况下将 long 除以浮点数
Objective C - divide long by a float without losing precision
我在用浮点数划分 long 类型时遇到了精度问题,结果弄乱了应用程序中的计算。假设我只是将毫秒转换为秒,因为这是我们的要求。下面是一个示例测试用例
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;
}
哪个最有可能return相同的值
此操作不应使用浮点数 - 因为以 10 为基数的浮点数表示形式总是产生奇数。更好的解决方案是使用定点运算并将所有内容乘以 1000(如果您需要更高的小数精度,则乘以更多)。这避免了浮点计算中固有的舍入误差。
参见:
为了更好地回答导致这些错误的原因。
您误解了浮点数的工作原理。浮点类型 float
和 double
是 binary 类型。
您的小数 0.095
等于 9/100 + 5/1000
- 所有小数都是 1/(10^n)
分数的总和。
现在考虑普通分数,1/3
的小数等于多少?它是 3/10 + 3/100 + 3/1000 + ...
或等价的 0.333...
,没有确切的 方式来表示它与小数相同。
对于类型 float
和 double
,小数部分是 1/(2^n)
分数的总和。与上述类似,并非所有的小数都可以精确地写成二进制小数。
除了使用不同的基数,十进制和二进制引起的差异外,计算机浮点数在任何数字中的位数都是有限的,而实际数学是无限的,因此计算可能不会产生精确 结果。
出于上述原因,最好的浮点实践是永远不要测试完全相等,而是测试两个数字的差异是否小于一个小值(这可能会因数字的大小而异)。在您的情况下,如果两个数字的差异小于例如 0.1 毫秒,您可能会决定这两个数字是 "equal"。
现在说完你给出的特定示例中的所有内容,如果更正,数字实际上是相同的,但两者都不是 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
应该是小到足以让您认为这两个值 "equal".
的几分之一秒
如果您要从事需要精度的工作,那么您真的应该阅读浮点运算,因为随着您进行更多的运算,由于基数和有限数字引起的差异会累积,并且有一些技术可以最小化和处理这些。
HTH
我在用浮点数划分 long 类型时遇到了精度问题,结果弄乱了应用程序中的计算。假设我只是将毫秒转换为秒,因为这是我们的要求。下面是一个示例测试用例
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;
}
哪个最有可能return相同的值
此操作不应使用浮点数 - 因为以 10 为基数的浮点数表示形式总是产生奇数。更好的解决方案是使用定点运算并将所有内容乘以 1000(如果您需要更高的小数精度,则乘以更多)。这避免了浮点计算中固有的舍入误差。
参见:
为了更好地回答导致这些错误的原因。
您误解了浮点数的工作原理。浮点类型 float
和 double
是 binary 类型。
您的小数 0.095
等于 9/100 + 5/1000
- 所有小数都是 1/(10^n)
分数的总和。
现在考虑普通分数,1/3
的小数等于多少?它是 3/10 + 3/100 + 3/1000 + ...
或等价的 0.333...
,没有确切的 方式来表示它与小数相同。
对于类型 float
和 double
,小数部分是 1/(2^n)
分数的总和。与上述类似,并非所有的小数都可以精确地写成二进制小数。
除了使用不同的基数,十进制和二进制引起的差异外,计算机浮点数在任何数字中的位数都是有限的,而实际数学是无限的,因此计算可能不会产生精确 结果。
出于上述原因,最好的浮点实践是永远不要测试完全相等,而是测试两个数字的差异是否小于一个小值(这可能会因数字的大小而异)。在您的情况下,如果两个数字的差异小于例如 0.1 毫秒,您可能会决定这两个数字是 "equal"。
现在说完你给出的特定示例中的所有内容,如果更正,数字实际上是相同的,但两者都不是 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
应该是小到足以让您认为这两个值 "equal".
如果您要从事需要精度的工作,那么您真的应该阅读浮点运算,因为随着您进行更多的运算,由于基数和有限数字引起的差异会累积,并且有一些技术可以最小化和处理这些。
HTH