我的Matlab脚本读取一个字符串值"0.001044397222448〃;从文件中,解析文件后,控制台中打印的值显示为双精度:
value_double =
0.001044397222448
在我使用value_float = single(value_double)
将这个数字转换为singe后,值显示为:
value_float =
0.0010444
这个变量的实际值是多少,我以后在Simulink模拟中使用它?它真的被截断/取整为0.0010444
吗?
我的问题是,稍后,在我将其与类似的C代码进行比较后,我会发现差异。在C代码中,该值读取为float gf = 0.001044397222448f;
,并打印为0.001044397242367267608642578125000
。因此C代码保持了良好的精度。但是,Matlab呢?
数字0.001044397222448(与绝大多数小数点一样)不能用二进制浮点精确表示。
作为一个单精度浮点,它最接近地表示为(十六进制)0x0.88e428 × 2
-9
,十进制值为0.00104439724236726760642578125。
在双精度中,它最接近地表示为0x0.88e427d4327300 × 2
-9
,十进制为0.0010443972224479999844017118745366460643708705902099609375。
这就是C和Matlab内部的数字。
你看到的其他一切都是数字打印出来的假象,可能是四舍五入和/或截断的。
当我说单精度表示";十进制为0.0010443972423672676087642578125〃;,这有点误导,因为它看起来有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.001043972244799998,也可以有0.0010443972224480002,但不能有任何介于两者之间的内容。(使用float
会稍微不那么接近,使用long double
也会非常接近,但永远不会得到确切的值。)
在C中,无论您使用的是float
还是double
,在使用%f
打印东西时,您都可以要求尽可能小的精度或尽可能高的精度,在高质量的实现下,您总是会得到适当的四舍五入结果。(当然,你得到的结果总是四舍五入实际内部值的结果,而不一定是你开始使用的十进制值。)例如,如果我运行以下代码:
printf("%.5fn", 0.001044397222448);
printf("%.10fn", 0.001044397222448);
printf("%.15fn", 0.001044397222448);
printf("%.20fn", 0.001044397222448);
printf("%.30fn", 0.001044397222448);
printf("%.40fn", 0.001044397222448);
printf("%.50fn", 0.001044397222448);
printf("%.60fn", 0.001044397222448);
printf("%.70fn", 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.001043972。
在C代码中,该值读取为float gf=0.001044397222448f;打印结果为0.0010443972423672676087642578125000
所以C得到了和我一样的答案——但是,这些数字中的大多数都没有意义。
因此C代码保持了良好的精度。但是,Matlab呢?
我敢打赌,Matlab对普通浮点和双精度保持相同的内部精度。
MATLAB使用IEEE-754二进制64作为双精度类型,使用二进制32作为单精度类型。当0.001044397222448四舍五入到二进制64中表示的最接近值时,结果为4816432068447840•2−62=0.0010443927224479999844017118745366460643708705902099609375。
当四舍五入到二进制32中可表示的最接近值时,结果为8971304•2−33=0.0104439724236726760864578125。
各种软件(C、Matlab等)以不同的方式显示浮点数,数字或多或少。根据IEEE 754规范,上述值是由浮点数据表示的确切数字,并且它们是在算术运算中使用数据时的值。
所有单个精度都应该相同
事情是这样的。根据文件,matlab和C都符合IEEE 754标准。这意味着实际存储在内存中的内容应该没有任何区别。
你可以手工计算二进制表示,但根据这个(感谢@Danijel)方便的网站,0.001044397222448
的表示应该是0x3a88e428
。
问题是你的表述有多精确?浮点有点棘手,但简短的答案是您的数字精确到小数点后第9位,的小数点表示到小数点第33位 打印时没有看到相同的东西,这并不意味着内存中没有相同的位(在C和MATLAB中,内存中应该有完全相同的字节)。您在显示器上看到差异的唯一原因是打印功能截断了您的数字。如果你用每种语言打印33个小数,你应该没有任何区别。 现在更详细地说,问题是:这种表述有多精确?浮点的棘手之处在于,表示的精度取决于所表示的数字。该表示超过32位,符号用1位除,指数用8位除,分数用23位除。 该数字可以计算为 我知道这是一个相当简短的解释。关于更多细节,这篇文章更准确地解释了浮点不精确性,这个网站给了你一些有用的信息,让你可以直观地使用表示。显示问题
fprintf('%.33f', value_float);
printf('%.33fn', gf);
关于浮点精度
sign * 2^(exponent-127) * 1.fraction
。这基本上意味着最大误差/精度(取决于您想要如何调用它)基本上是2^(exponent-127-23)
,这里的23表示分数的23个字节。(有一些边缘案例,我不详细说明)。在我们的例子中,指数是117
,这意味着您的精度是2^(117-127-23) = 1.16415321826934814453125e-10
。这意味着你的单精度浮点应该准确地表示你的数字,直到小数点后9位,之后就取决于运气了。更多详细信息