我似乎在Objective-C中遇到一个奇怪的问题,将浮点数转换为NSNumber(为方便起见将其包装),然后将其转换回浮点数。
简而言之,我的一个类有一个属性red
,这是一个从0.0
到1.0
的浮动:
@property (nonatomic, assign) float red;
此对象将自身与从磁盘加载的值进行比较,以进行同步。 (该文件可以在应用程序外部进行更改,因此它会定期检查文件更改,将备用版本加载到内存中,并进行比较,合并差异。)
这是一个有趣的片段,其中比较了两个值:
if (localObject.red != remoteObject.red) {
NSLog(@"Local red: %f Remote red: %f", localObject.red, remoteObject.red);
}
以下是我在日志中看到的内容:
2011-10-28 21:07:02.356 MyApp[12826:aa63] Local red: 0.205837 Remote red: 0.205837
怪异。对?这段代码是如何执行的?
存储在文件中的实际值:
...red="0.205837"...
使用:
转换为float
currentObject.red = [[attributeDict valueForKey:@"red"] floatValue];
在代码中的另一个点,我能够从GDB获取屏幕截图。它被打印到NSLog中:(这也是它在磁盘上文件中出现的精度。)
2011-10-28 21:21:19.894 MyApp[13214:1c03] Local red: 0.707199 Remote red: 0.707199
但在调试器中显示为:
如何在属性级别获取此级别的精度,但不是存储在文件中,还是在NSLog中正确打印? 为什么它似乎变化了?
答案 0 :(得分:5)
如果您在任何时候将其转换为字符串,请尝试使用%0.16f
代替%f
(或您想要的任何精度而不是.16
)。
有关详细信息,请参阅IEEE Std formatting。
此外,使用objectForKey
代替valueForKey
(valueForKey
不适用于词典):
currentObject.red = [[attributeDict objectForKey:@"red"] floatValue];
有关objectForKey
与valueForKey
的更好解释,请参阅此SO答案:
答案 1 :(得分:4)
您遇到的问题是浮点问题。浮点数并不完全代表存储的数字(除了一些在这里无关紧要的特定情况)。链接craig posted中的示例就是一个很好的例子。
在您的代码中,当您将值写入文件时,您会写出浮点数中存储的内容的近似值。当你加载它时,它的另一个近似值存储在浮点数中。然而,这两个数字不太可能相等。
最佳解决方案是使用两个浮点数的模糊比较。我不是一个客观的程序员,所以我不知道语言是否包含内置函数来进行这种比较。但是this link提供了一组很好的示例,说明了进行此比较的各种方法。
您还可以尝试使用更高精度的其他发布解决方案来写出您的文件,但您可能最终会浪费空间来获得您不需要的额外精度。我个人建议你使用模糊比较,因为它更具防弹性。
答案 2 :(得分:2)
您说“远程”值是“从磁盘加载”。我猜测磁盘上的表示不是IEEE浮点值,而是某种字符表示或某种类型。因此,考虑到IEEE float的工作方式,存在不可避免的转换错误。考虑到浮点值中只有大约6位十进制精度,你不会得到一个确切的结果,但它很少映射到正好6个十进制数字,而是有点像十进制的1/3 - 没有确切的映射。
答案 3 :(得分:0)