转换“float”精度损失;到 NSNumber,回到“float”
我似乎在 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 中正确打印? 为什么它看起来有所不同?
I seem to be encountering a strange issue in Objective-C converting a float to an NSNumber (wrapping it for convenience) and then converting it back to a float.
In a nutshell, a class of mine has a property red
, which is a float from 0.0
to 1.0
:
@property (nonatomic, assign) float red;
This object is comparing itself to a value that is loaded from disk, for synchronization purposes. (The file can change outside the application, so it checks periodically for file changes, loads the alternate version into memory, and does a comparison, merging differences.)
Here's an interesting snippet where the two values are compared:
if (localObject.red != remoteObject.red) {
NSLog(@"Local red: %f Remote red: %f", localObject.red, remoteObject.red);
}
Here's what I see in the logs:
2011-10-28 21:07:02.356 MyApp[12826:aa63] Local red: 0.205837 Remote red: 0.205837
Weird. Right? How is this piece of code being executed?
The actual value as stored in the file:
...red="0.205837"...
Is converted to a float
using:
currentObject.red = [[attributeDict valueForKey:@"red"] floatValue];
At another point in the code I was able to snag a screenshot from GDB. It was printed to NSLog as: (This is also the precision with which it appears in the file on disk.)
2011-10-28 21:21:19.894 MyApp[13214:1c03] Local red: 0.707199 Remote red: 0.707199
But appears in the debugger as:
How is this level of precision being obtained at the property level, but not stored in the file, or printed properly in NSLog? And why does it seem to be varying?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您遇到的问题是浮点问题。浮点数并不完全代表存储的数字(除了一些在这里无关紧要的特定情况)。链接中的示例 craig 发布了< /a> 就是一个很好的例子。
在代码中,当您将值写入文件时,您会写入浮点数中存储的近似值。当您将其加载回来时,它的另一个近似值将存储在浮点数中。然而,这两个数字不太可能相等。
最好的解决方案是使用两个浮点数的模糊比较。我不是一个客观的 C 程序员,所以我不知道这些语言是否包含内置函数来执行此比较。然而此链接提供了一组关于各种执行方法的很好的示例这个比较。
您还可以尝试使用更大的精度写入文件的其他发布的解决方案,但您可能最终会浪费空间来获得不需要的额外精度。我个人建议您使用模糊比较,因为它更防弹。
The problem your are experiencing is a problem with floating point. A floating point number doesn't exactly represent the number stored (except for some specific cases which don't matter here). The example in the link craig posted is an excellent example of this.
In your code, when you write out the value to your file you write an approximation of what is stored in the floating point number. When you load it back, another approximation of it is stored in the float. However these two numbers are unlikely to be equal.
The best solution is to use a fuzzy comparison of the two floating point numbers. I'm not an objective c programmer, so I don't know if the languages includes builtin functions to preform this comparison. However this link provides a good set of examples on various ways to preform this comparison.
You can also try the other posted solution of using a bigger precision to write out to your file, but you will probably end up wasting space for the extra precision that you don't need. I'd personally recommend you use the fuzzy comparison as it is more bullet proof.
您说“远程”值是“从磁盘加载”。我猜测磁盘上的表示不是 IEEE 浮点位值,而是某种字符表示或类似的表示。因此,考虑到 IEEE 浮点数的工作方式,该表示形式之间不可避免地会出现转换错误。由于浮点值中只有大约 6 位小数精度,因此您不会得到准确的结果,但它很少映射到精确的 6 位小数,而是有点像表示十进制中的 1/3 - 没有精确的结果映射。
You say that the "remote" value is "loaded from disk". I'm guessing that the representation on disk is not an IEEE float bit value, but rather some sort of character representation or some such. So there are inevitable conversion errors going to and from that representation, given the way IEEE float works. You will not get an exact result, given that there's only about 6 digits of decimal precision in a float value, but it rarely maps to exactly 6 decimal digits but instead is sort of like representing 1/3 in decimal -- there is no exact mapping.
阅读此内容:http://floating-point-gui.de/
Read this: http://floating-point-gui.de/
如果您在任何时候将其转换为字符串或从字符串转换,请尝试使用
%0.16f
而不是%f
(或任何您想要的精度而不是.16< /代码>)。
有关详细信息,请参阅IEEE Std 格式。
另外,使用
objectForKey
而不是valueForKey
(valueForKey
不适用于字典):请参阅此 SO 答案以更好地解释 <代码>objectForKey与
valueForKey
:objectForKey 和 valueForKey 之间的区别?
If you are converting it to/from a string at any point try using
%0.16f
instead of%f
(or whatever precision you want instead of.16
).For more info, see IEEE Std formatting.
Also, use
objectForKey
instead ofvalueForKey
(valueForKey
is not intended to be used on dictionaries):See this SO answer for a better explanation of
objectForKey
vsvalueForKey
:Difference between objectForKey and valueForKey?