c - 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 呢?
解决方案
数字 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 对普通浮点数和双精度数保持相同的内部精度。
推荐阅读
- flutter - Flutter 如何将参数传递给“get”方法?
- r - 根据季度观察计算年化回报
- python - 将项目附加到来自 xml 的 dict 列表
- jquery - 如何使用 jquery 和 while 循环动态分配 id
- javascript - 从嵌套数组中删除无子元素(叶子除外)
- wordpress - 如何删除 wordpress 页面中的上次更新日期?
- jenkins - 无法从浏览器连接到 jenkins
- java - 多对多的 JPA 标准规范
- javascript - 如何在 Google TimeLine 图表中仅在一行中设置一个栏(带有分组的行标签)?
- python - 在运行时动态加载客户端库的大多数 Pythonic/Django 方式