浮点精度与实际分配的值不匹配?

时间:2018-02-21 11:18:48

标签: c floating-point

我正在研究STM32F777 mcu在FreeRTOS之上创建应用程序。 STM32F777带有双精度浮点单元。我在使用浮点单元时发现这个非常烦人的问题,当我使用常量或其他东西为浮点值赋值时,例如:

float x = 78.352361;

我在调试时看到分配给变量x的实际值是: 78.3523636

同样,当我将 17.448636 分配给变量x时,我在调试时实际看到的分配是 17.4486351

任何想法,为什么会发生这种情况以及如何解决这个问题?我希望将正确的精度保持到6位数。

4 个答案:

答案 0 :(得分:5)

最常见的浮点格式IEEE 754包括基本的32位格式和基本的64位格式,这些格式通常用于C中的floatdouble类型为简洁起见,我会在这个答案中称他们为floatdouble

这些类型中的任何一种都不能精确地表示非整数,而不是2的幂的倍数(例如,¼,¾,1 / 1024,33 / 1048576)。当从十进制转换为floatdouble时,其他所有数字都会略有变化。

但是,float具有将任意十进制数字与六位有效数字(例如1.2345或9.87654e23)四舍五入到float的属性,并返回六位有效十进制数字返回原始数字(提供数字在格式的正常范围内)。在C中,这个位数由值FLT_DIG报告,由float.h定义。由于您的电话号码78.352361有8位有效数字,不能保证转换到float后可以保持不变。

对于doubleDBL_DIG报告,往返至少可以保留15位数字。

请注意,这是由于一次转换为二进制浮点并返回原始小数位数而导致的四舍五入的数字。如果在浮点中执行附加算术运算,则会发生其他舍入,这可能会累积更多错误。并且,如果使用比原始值更多的十进制数字格式化值,则结果可能与原始数字不同。 (例如,.9f转换回一位十进制数时产生“.9”,转换为九位十进制数时产生“0.899999976”。)

由于double保证在往返中幸存15位数,因此您的号码78.352361可以在转换为double后保持不变并返回8位有效数字。此外,有足够的精度来执行某些算术,而不会累积太多错误,以便在八位有效十进制数字中可见。但是,浮点运算可能很棘手,完整的错误分析取决于您执行的操作。

答案 1 :(得分:1)

浮点变量不能存储任意实数,它们只能存储实数的特定子集(基数略低于2 ^ 32,对于32位IEEE浮点数)的成员。 "变化"您看到的是因为您分配的值不是float可以存储在系统上的值之一。它被四舍五入到可以存储的值。

为了完美地存储6个小数位,对于纬度或经度,你应该使用32位整数(代表存储值的100万倍)。

答案 2 :(得分:1)

你问,“有没有办法解决这个问题?”基本上有两种方式:

  1. 了解浮点的局限性,并学习如何在浮点内工作。
  2. 使用固定点,而不是浮点。
  3. 理解浮点的基本要点是没有这样的数字,比如17.445652十进制数。也没有17.445651999999999这样的号码。在内部,实际的二进制浮点数介于两者之间。就是那样子。因此,如果您使用浮点数,并且您想要,例如,六位数的精度,并且如果您使用的格式保证六位数的精度(double做的那种,那么),你就是总是需要适当的回合。 (如果您的调试器显示未设置的值,您可能还必须记住不要混淆。)

    或者,如果由于某种原因舍入是不可接受的,您可以使用固定点,但您必须手动执行,因为它不是内置于语言中。将所有纬度和经度乘以1000000,并将其作为long int内部存储,当您将它们打印出来时,请从最后的'.' 6个位置开始。例如:

    long int latitude = 17445652;    /* microdegrees */
    
    printf("latitude = %ld.%06ld\n", latitude / 1000000, latitude % 1000000);
    

答案 3 :(得分:1)

在你发表的评论中,

  

您能否告诉我如何根据纬度/经度值计算精度?就像十进制数字后面的位置说明距离的精确程度一样?

这显然是一个完全独立的问题,我可能不应该在这里回答,但这是一个有趣的问题,所以我无法抗拒。你可以从最初的原则中解决它。

海里最初被定义为赤道的一分钟经度。所以地球的周长是360 x 60 = 21600海里。海里是1852米,所以这是40003200米,或40003.2公里。这是一个很好的结果,因为我认为,这个仪表最初的定义是,地球的圆周到极点的1/40000000,所以我们在正确的轨道上。

随着地球的周长(显然是360度),我们可以计算出这些距离:

degrees     distance (m)
1           111120
0.1         11112
0.01        1111.2
0.001       111.12
0.0001      11.112
0.00001     1.1112
0.000001    0.1111
0.0000001   0.0111
0.00000001  0.0011

所以0.000001度约为0.1米,或10厘米。而0.00000001度约为1毫米。

这些数字都适用于赤道的经度或任何地方的纬度。对于不在赤道的经度,乘以纬度的余弦。 (例如,我居住在纬度42.这里,0.000001度经度约为0.111 x 0.743 = 0.082米= 8厘米。)

这些都是近似结果。地球不是一个完美的球体,当你开始进行精确的导航时,事实证明这可以产生很大的不同,尽管与我在这里展示的数字相差不多。