Matlab "single" 精度与 C 浮点数?

Matlab "single" precision vs C floating point?

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

  value_double =
      0.001044397222448

我用value_float = single(value_double)把这个数字转成单数后,数值显示为:

value_float = 
  0.0010444

我稍后在 Simulink 仿真中使用的这个变量的实际值是多少?真的是truncated/rounded到0.0010444吗?

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

MATLAB 使用 IEEE-754 binary64 作为双精度类型,使用 binary32 作为单精度类型。当 0.001044397222448 四舍五入到 binary64 中可表示的最接近值时,结果为 4816432068447840•2−62 = 0.00104439722244799998440711874536646064370870590230759.0[0.00104439722244799998440711874536646064370870590230759.0

当它四舍五入到二进制 32 中可表示的最接近值时,结果为 8971304•2−33 = 0.001044397242367267608642578125。

各种软件(C、Matlab 等)以不同的方式显示浮点数,位数更多或更少。根据 IEEE 754 规范,上述值是由浮点数据表示的确切数字,它们是数据在算术运算中使用时的值。

所有单精度应该相同

事情是这样的。根据文档,两者都是 matlab and C comply with the IEEE 754 标准。这意味着内存中实际存储的内容应该没有任何区别。

您可以手动计算二进制表示,但根据 this(感谢@Danijel)方便的网站,0.001044397222448 的表示应该是 0x3a88e428

问题是您的表述有多精确?浮点数有点棘手,但简短的回答是你的数字精确到小数点后第 9 位并且小数表示到小数点后第 33 位.如果你想要长答案,请参阅本 post.

末尾的两段

显示问题

您在打印时没有看到相同的东西这一事实并不意味着您在内存中没有相同的位(并且在 C 和 MATLAB 中您应该在内存中具有完全相同的字节)。您在显示器上看到差异的唯一原因是打印功能截断了您的号码。如果你用每种语言打印 33 位小数,你应该没有任何区别。

  • 要在 matlab 中使用:fprintf('%.33f', value_float);
  • 要在 c 中使用 printf('%.33f\n', gf);

关于浮点精度

现在更详细地说,问题是:这种表示有多精确?好吧,浮点数的棘手之处在于表示的精度取决于你所代表的数字。表示超过 32 位,除以 1 位符号,8 位指数和 23 位小数。

该数字可以计算为sign * 2^(exponent-127) * 1.fraction。这基本上意味着最大 error/precision (取决于你想如何称呼它)基本上是 2^(exponent-127-23),这里的 23 代表小数的 23 个字节。 (有一些边缘情况,我不会详细说明)。在我们的例子中,指数是 117,这意味着你的精度是 2^(117-127-23) = 1.16415321826934814453125e-10。这意味着您的单精度浮点数应该准确地表示您的数字,直到小数点后第 9 位,之后就看运气了。

更多详情

我知道这是一个相当简短的解释。有关更多详细信息,this post explains the floating point imprecision more precisely and this website 为您提供了一些有用的信息,并允许您直观地使用表示。

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

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

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

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

你看到的其他一切都是数字打印出来的产物,可能是四舍五入的 and/or t运行cated。

当我说单精度表示“十进制为 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 是如何打印东西的。


回答您的具体问题:

What is the real value of this variable, that I later use in my Simulink simulation? Is it really truncated/rounded to 0.0010444?

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

In the C code the value is read as float gf = 0.001044397222448f; and it prints out as 0.001044397242367267608642578125000

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

So the C code keeps good precision. But, does Matlab?

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