转换“float”精度损失;到 NSNumber,回到“float”

发布于 2024-12-12 12:50:18 字数 1205 浏览 0 评论 0原文

我似乎在 Objective-C 将浮点数转换为 NSNumber (为了方便起见将其包装)然后将其转换回浮点数时遇到了一个奇怪的问题。

简而言之,我的一个类有一个属性 red,它是一个从 0.01.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

但在调试器中显示为:

GDB Screenshot

如何在属性级别获得这种精度级别,但不存储在文件中,或在 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:

GDB Screenshot

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

递刀给你 2024-12-19 12:50:19

您遇到的问题是浮点问题。浮点数并不完全代表存储的数字(除了一些在这里无关紧要的特定情况)。链接中的示例 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.

暮年 2024-12-19 12:50:19

您说“远程”值是“从磁盘加载”。我猜测磁盘上的表示不是 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.

世界等同你 2024-12-19 12:50:18

如果您在任何时候将其转换为字符串或从字符串转换,请尝试使用 %0.16f 而不是 %f (或任何您想要的精度而不是 .16< /代码>)。

有关详细信息,请参阅IEEE Std 格式


另外,使用 objectForKey 而不是 valueForKeyvalueForKey 不适用于字典):

currentObject.red = [[attributeDict objectForKey:@"red"] floatValue];

请参阅此 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 of valueForKey (valueForKey is not intended to be used on dictionaries):

currentObject.red = [[attributeDict objectForKey:@"red"] floatValue];

See this SO answer for a better explanation of objectForKey vs valueForKey:

Difference between objectForKey and valueForKey?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文