首页 > 解决方案 > Matlab“单”精度与C浮点?

问题描述

我的 Matlab 脚本从文件中读取字符串值“0.001044397222448”,解析文件后,控制台中打印的该值显示为双精度:

  value_double =
      0.001044397222448

使用 将此数字转换为单数后value_float = single(value_double),该值显示为:

value_float = 
  0.0010444

这个变量的实际值是多少,我稍后会在 Simulink 仿真中使用它?它真的被截断/四舍五入0.0010444吗?

我的问题是,稍后,在我将其与类似的 C 代码进行比较之后,我得到了差异。在 C 代码中,值被读取为float gf = 0.001044397222448f;并打印为0.001044397242367267608642578125000. 因此 C 代码保持良好的精度。但是,Matlab 呢?

标签: cmatlabfloating-pointdoublesimulink

解决方案


数字 0.001044397222448(与绝大多数十进制小数一样)不能用二进制浮点数精确表示。

作为单精度浮点数,它最接近地表示为 (hex) 0x0.88e428 × 2-9,十进制为 0.001044397242367267608642578125。

在双精度中,它最接近表示为0x0.88e427d4327300 × 2-9,十进制为 0.001044397222447999984407118745366460643708705902099609375。

这些是 C 和 Matlab 内部的数字。

您看到的其他所有内容都是数字如何打印出来的伪影,可能是四舍五入和/或截断的。

当我说单精度表示“十进制为 0.001044397242367267608642578125”时,这有点误导,因为它看起来像是有 28 位或更多位的精度。然而,这些数字中的大多数是从基数 2 转换回基数 10 的产物。正如其他答案所指出的那样,单精度浮点实际上只为您提供大约 7 个十进制数字的精度,您可以看到您是否注意到其中单精度和双精度等价物开始发散:

0.001044397242367267608642578125
0.001044397222447999984407118745366460643708705902099609375
            ^
        difference

同样,双精度为您​​提供大约 16 位十进制数字的精度,如果您比较转换几个前一个和下一个尾数值的结果,您可以看到:

0x0.88e427d43272f8  0.00104439722244799976756668424826557384221814572811126708984375
0x0.88e427d4327300  0.001044397222447999984407118745366460643708705902099609375
0x0.88e427d4327308  0.00104439722244800020124755324246734744519926607608795166015625
0x0.88e427d4327310  0.0010443972224480004180879877395682342466898262500762939453125
                                        ^
                                     changes

这也说明了为什么你永远不能用二进制精确地表示你的原始值 0.001044397222448。如果你使用double,你可以有 0.00104439722244799998,或者你可以有 0.0010443972224480002,但你不能有任何介于两者之间的东西。(您与 的接近度会降低一些float,并且您可以与接近得多long double,但您永远不会得到您的确切值。)

在 C 语言中,无论您使用的是float还是double,在使用 打印内容时,您都可以要求尽可能少或尽可能多的精度%f,并且在高质量的实现下,您将始终获得正确舍入的结果。(当然,你得到的结果总是四舍五入的结果,内部值,不一定是你开始的十进制值。)例如,如果我运行这个代码:

printf("%.5f\n", 0.001044397222448);
printf("%.10f\n", 0.001044397222448);
printf("%.15f\n", 0.001044397222448);
printf("%.20f\n", 0.001044397222448);
printf("%.30f\n", 0.001044397222448);
printf("%.40f\n", 0.001044397222448);
printf("%.50f\n", 0.001044397222448);
printf("%.60f\n", 0.001044397222448);
printf("%.70f\n", 0.001044397222448);

我看到了这些结果,正如您所看到的,它们与上面的分析相匹配。(请注意,此特定示例使用的是double,而不是float。)

0.00104
0.0010443972
0.001044397222448
0.00104439722244799998
0.001044397222447999984407118745
0.0010443972224479999844071187453664606437
0.00104439722244799998440711874536646064370870590210
0.001044397222447999984407118745366460643708705902099609375000
0.0010443972224479999844071187453664606437087059020996093750000000000000

我不确定 Matlab 是如何打印的。


在回答您的具体问题时:

这个变量的实际值是多少,我稍后会在 Simulink 仿真中使用它?它真的被截断/四舍五入0.0010444吗?

作为一个浮点数,它实际上被“截断”为一个数字,转换回十进制,正好是 0.001044397242367267608642578125。但正如我们所见,这些数字中的大多数基本上是没有意义的,结果可以更恰当地认为是大约 0.0010443972。

在 C 代码中,该值被读取为 float gf = 0.001044397222448f; 它打印为 0.001044397242367267608642578125000

所以 C 得到了与我相同的答案——但同样,这些数字中的大多数都没有意义。

因此 C 代码保持良好的精度。但是,Matlab 呢?

我敢打赌,Matlab 对普通浮点数和双精度数保持相同的内部精度。


推荐阅读