查看: 906|回复: 3|关注: 0

[话题讨论] 浮点数在内存中表示方式的理解

[复制链接]

论坛优秀回答者

18

主题

1988

帖子

420

最佳答案
  • 关注者: 81
发表于 2019-5-22 10:01:13 | 显示全部楼层 |阅读模式
本帖最后由 深蓝孩童 于 2019-5-22 10:09 编辑

论坛有很多讨论浮点数误差的帖子,会说到浮点数的表示方法。这里简要记录一下我的理解,因为重复了对于浮点数学习的过程,觉得还是需要有一些记录,主要是single和double类型的在内存中表示方法的验证。
参考文档https://blogs.mathworks.com/cleve/2014/07/07/floating-point-numbers/?s_tid=srchtitle
https://blogs.mathworks.com/loren/2006/08/23/a-glimpse-into-floating-point-accuracy/?s_tid=srchtitle
https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/company/newsletters/news_notes/pdf/Fall96Cleve.pdf
  1. number_dec = single(120.45)
  2. hexString = num2hex(number_dec) % sprintf('%tx', number_dec)
  3. binString = dec2bin(hex2dec(hexString));
  4. binString = [char(zeros(1,32-length(binString)) + 48) binString]
  5. number_transform = (1 + single(bin2dec(binString(10:end)))/2^23)*2^(bin2dec(binString(2:9)) - 127) % 按照single数据的表示方式来求得十六进制对应的浮点数
复制代码
  1. number_dec = 8.5
  2. hexString = num2hex(number_dec) % sprintf('%bx', number_dec)
  3. binString = dec2bin(hex2dec(hexString));
  4. binString = [char(zeros(1,64-length(binString)) + 48) binString]
  5. (1 + bin2dec(binString(13:end))/2^52)*2^(bin2dec(binString(2:12)) - 1023) % 按照double数据的表示方式来求得十六进制对应的浮点数
  6. hex2num(hexString) % 十六进制转化为浮点数
复制代码

再有julia的相关代码:


  1. # half类型表示方式
  2. a = Float16(8.5f0)
  3. println("数值:", a, " 类型:", typeof(a))
  4. s = bitstring(a)
  5. println("十六进制表示:0x", string(parse(UInt16, s, base=2), base=16, pad=4))
  6. println("bitstring: ", s, " 比特长度:", length(s))
  7. print("第一个比特:", s[1])
  8. print(" 阶码比特:", s[2:6])
  9. println(" 尾数比特:", s[7:end])
  10. exp = parse(UInt16, s[2:6], base = 2)
  11. res = parse(UInt64, s[7:end], base = 2)
  12. println((1 + res/2^10)*2.0^(exp - 15))
  13. # single类型表示方式
  14. println(repeat("#", 64))
  15. a = 8.5f0
  16. println("数值:", a, " 类型:", typeof(a))
  17. s = bitstring(a)
  18. println("十六进制表示:0x", string(parse(UInt32, s, base=2), base=16, pad=4))
  19. println("bitstring: ", s, " 比特长度:", length(s))
  20. print("第一个比特:", s[1])
  21. print(" 阶码比特:", s[2:9])
  22. println(" 尾数比特:", s[10:end])
  23. exp = parse(UInt8, s[2:9], base = 2)
  24. res = parse(UInt32, s[10:end], base = 2)
  25. println((1 + res/2^23)*2^(exp - 127))
  26. # double类型表示方式
  27. println(repeat("#", 64))
  28. a = 8.5
  29. println("数值:", a, " 类型:", typeof(a))
  30. s = bitstring(a)
  31. println("十六进制表示:0x", string(parse(UInt64, s, base=2), base=16, pad=8))
  32. println("bitstring: ", s, " 比特长度:", length(s))
  33. print("第一个比特:", s[1])
  34. print(" 阶码比特:", s[2:12])
  35. println(" 尾数比特:", s[13:end])
  36. exp = parse(UInt16, s[2:12], base = 2)
  37. res = parse(UInt64, s[13:end], base = 2)
  38. println((1 + res/2^52)*2^(exp - 1023))
  39. # 十六进制转化为浮点数
  40. nTemp = 0x00010101
  41. println(typeof(nTemp))
  42. reinterpret(Float32, nTemp)
复制代码
C++代码(参考网上博客)
  1. #include <iostream>

  2. using namespace std;

  3. int main()
  4. {
  5.         float single_number = 120.45f;
  6.         unsigned char* single_number_ptr = (unsigned char*)& single_number;
  7.         for (int i = 3; i >= 0; i--)
  8.                 printf("%2X", single_number_ptr[i]);

  9.         cout << endl;

  10.         double double_number = 120.45;
  11.         unsigned char* double_number_ptr = (unsigned char*)& double_number;
  12.         for (int i = 7; i >= 0; i--)
  13.                 printf("%2X", double_number_ptr[i]);

  14.         return 0;
  15. }
复制代码
相应的结果分别为:
matlab_19a win10.png julia 1.1,win10.png c++ vs2019 win10.png




多看帮助文档
说明你的matlab版本

论坛优秀回答者

18

主题

1988

帖子

420

最佳答案
  • 关注者: 81
 楼主| 发表于 2019-5-26 15:17:46 | 显示全部楼层
本帖最后由 深蓝孩童 于 2019-6-22 18:50 编辑

由浮点数表示方式知道,double类型的指数范围为[-1022,1023],即十一个比特部分为0b000'0000'0001~0b111'1111'1110,同时尾数部分可以包括所有组合,即范围为[0,2^52-1],代表小数部分是[0,(2^52-1)/2^52]。
double类型指数部分两个端点值0和1024用来表示特殊的浮点数,指数为1024,小数为0时,表示inf;指数为1024,小数非0时,表示nan,见下图。指数部分都为0的情况见一楼的参考文档2。
批注 2019-05-26 151711.png


多看帮助文档
说明你的matlab版本

论坛优秀回答者

18

主题

1988

帖子

420

最佳答案
  • 关注者: 81
 楼主| 发表于 2019-7-31 16:51:20 | 显示全部楼层
本帖最后由 深蓝孩童 于 2019-7-31 16:59 编辑

手算浮点数的二进制表示的方法,以单精度浮点为例:

十进制浮点转二进制的表示 https://blog.csdn.net/dbhllnr/article/details/51956134
十进制小数的二进制表示:
整数部分:除以2,取出余数,商继续除以2,直到得到0为止,将取出的余数逆序。
小数部分:乘以2,取整,小数部分继续乘以2,取整,得到小数部分0为止,将整数顺序排列。
例如52.625,整数部分应该为110100,小数部分应该为101,所以可以写为110100.101,相应的移位之后可以写为1.10100101*2^5,因为浮点数表示就是用1+f作为科学计数法的基数
即相应的f的二进制表示为10100101,指数部分为5+127=132,二进制为10000100,即浮点数表示为0 10000100 10100101000000000000000
此问题来源于旷视的一个题:
IEEE754 规定规约型单精度浮点数由 1 位符号位,8 位指数值和 23 位分数值构成。指数偏移值为 127,分数值默认整数部分为 1,只存储小数部分。例:十进制浮点数-12.5,转换为二进制为(-1100.1)2=(-1.1001)2×2^3,符号位为 1,指数值为3+127=130=0x82,分数值为 0x480000,连接后存储为 0xC1480000;问十进制浮点数52.625和十进制浮点数 526.25进行按位或操作得到的十进制浮点数是多少?

可以手算得到相应的二进制表示,然后进行按位或,然后再转化为单精度浮点数表示。

相应的MATLAB代码为:
  1. number_dec = single(526.25);
  2. hexString = num2hex(number_dec); % sprintf('%tx', number_dec)
  3. binString1 = dec2bin(hex2dec(hexString));
  4. binString1 = [char(zeros(1,32-length(binString1)) + 48) binString1];
  5. number_dec = single(52.625);
  6. hexString = num2hex(number_dec); % sprintf('%tx', number_dec)
  7. binString2 = dec2bin(hex2dec(hexString));
  8. binString2 = [char(zeros(1,32-length(binString2)) + 48) binString2];
  9. a = [0 binString1(2:9)-48 | binString2(2:9)-48 binString1(10:end)-48 | binString2(10:end)-48]; % 按位或
  10. aStr = char(a+48) % 按位或之后转化为字符向量,方便下边转换为相应的十进制
  11. (1+bin2dec(aStr(10:end))/2^23)*2^(bin2dec(aStr(2:9))-127)
复制代码
结果为:
1.png

多看帮助文档
说明你的matlab版本

新手

5 麦片

财富积分


050


0

主题

1

帖子

0

最佳答案
发表于 2019-8-22 20:47:47 | 显示全部楼层
66666666666666
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐上一条 /3 下一条

快速回复 返回顶部 返回列表