Modbus协议4个byte转成单精度float类型数据原理解析和基于Java的两种方式实现

7 篇文章 0 订阅
2 篇文章 2 订阅

前言

学习过Modbus协议的同学应该知道对于4x地址(保持寄存器)里面存的数据都是整数,那对于浮点数,Modbus协议是如何处理的呢?

举栗子:
在这里插入图片描述
浮点数26.3存储方式如下:
在这里插入图片描述
为什么要这样存储浮点数呢?

因为IEEE754标准(IEEE二进制浮点数算术标准)定义了两种浮点数格式:32位单精度和64位双精度,如下图所示:

在这里插入图片描述IEEE 754 的规定:

  • 32位单精度格式中包含1bit符号位s,8bit阶码e,23bit尾数f

  • 64位双精度格式中包含1bit符号位s,11bit阶码e,52bit尾数f

一个浮点数 (Value) 可以这样表示:

Value = sign * exponent * fraction

也就是浮点数的实际值,等于符号位(sign bit)乘以指数偏移值(exponent bias)再乘以分数值(fraction)。

原理分析

就拿上面的例子,浮点数26.3,用了两个寄存器保存,数值分别是1685026214,转成16进制分别是0x41D20x6666,那浮点数26.3可以用0x41D26666表示,将0x41D26666代入格式: SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
整个转化过程如下:



    0x41D26666 转二进制01000001110100100110011001100110

    S E E E  E E E E  E M M M  M M M M  M M M M  M M M M  M M M M  M M M M
	0 1 0 0  0 0 0 1  1 1 0 1  0 0 1 0  0 1 1 0  0 1 1 0  0 1 1 0  0 1 1 0
	
	逐步分解:
	1#符号位 
	S 
	0
	S = 0 0为正,1为负
	
	2#阶码:
	E E E  E E E E  E 
	1 0 0  0 0 0 1  1 
	EEEEEEEE = 10000011 = 131
	指数值e = (EEEEEEEE) - 127 = 4
	
	3#尾数
	M M M  M M M M  M M M M  M M M M  M M M M  M M M M
	1 0 1  0 0 1 0  0 1 1 0  0 1 1 0  0 1 1 0  0 1 1 0
	
	4#在尾数的左边有一个省略的小数点和1,这个1在浮点数的保存中经常省略,
    加上一个1和小数点到尾数的开头,得到尾数值如下:
	1.1 0 1  0 0 1 0  0 1 1 0  0 1 1 0  0 1 1 0  0 1 1 0
	
	5#根据指数值e调整尾数:一个负的指数值向左移动小数点,一个正的指数值向右移动小数点
	指数值e = 4,因此小数点向右移动4位,得到尾数值如下:
	1 1 0 1 0 . 0 1 0  0 1 1 0  0 1 1 0  0 1 1 0  0 1 1 0
	
	6#小数点左边代表整数,右边代表小数
	整数:1 1 0 1 0 
	小数:0 1 0 0 1 1 0  0 1 1 0  0 1 1 0  0 1 1 0
	
	7#整数部分换算
	整数:1 1 0 1 0 
	1 x 2^4 + 1 x 2^3 + 0 x 2^2 + 1 x 2^1 + 0 x 2^0 
	简化上面过程:
	2^4 + 2^3 + 2^1 = 26
	
	
	8#小数部分换算
	小数:0 1 0 0 1 1 0  0 1 1 0  0 1 1 0  0 1 1 0
	0 x 2^-1 + 1 x 2^-2 + 0 x 2^-3 + 0 x 2^-4 + 1 x 2^-5 + 1 x 2^-6 + 0 x 2^-7 + 0 x 2^-8 + 1 x 2^-9 + 1 x 2^-10 + 0 x 2^-11 + 0 x 2^-12 + 1 x 2^-13 + 1 x 2^-14 ....
	简化上面:
	2^-2 + 2^-5 + 2^-6 + 2^-9 + 2^-10 + 2^-13 + 2^-14 + 2^-17 + 2^-18 = 0.2999992370605469 = 0.3
	
	9#浮点数值 = 整数 + 小数 = 26 + 0.3 = 26.3 符号位=0,不用 x (-1)
	
	

将上面整个过程整合成这个公式:

   
    fValue = (-1) ^ S x 2 ^ (E - 127) x (1 + M)
    
    参数说明:
    fValue:非0浮点数数值
	S:数符,0正1负
	E: 阶码8位,以2为底,阶码 = 阶码真值 + 127
	M:尾数23位,采用隐含尾数最高位为1的表示方法,实际尾数24位,尾数真值 = 1 + 尾数

Java代码实现

  • 将上面的转换过程写成代码:

    public static float toFloat(short s1, short s2)
    {
        //将输入数值short转化为无符号unsigned short
        int us1 = s1, us2 = s2;
        if (s1 < 0) us1 += 65536;
        if (s2 < 0) us2 += 65536;
        //sign: 符号位, exponent: 阶码, mantissa:尾数
        int sign, exponent;
        float mantissa;
        //计算符号位
        sign = us1 / 32768;
        //去掉符号位
        int emCode = us1 % 32768;
        //计算阶码
        exponent = emCode / 128;
        //计算尾数
        mantissa = (float)(emCode % 128 * 65536 + us2) / 8388608;
        //代入公式 fValue = (-1) ^ S x 2 ^ (E - 127) x (1 + M)
        return (float)Math.pow(-1, sign) * (float)Math.pow(2, exponent - 127) * (1 + mantissa);
    }
    

测试结果:

> Task :Test.main()
请输入第一个值:
16850
请输入第二个值:
26214
转换结果:26.3

请输入第一个值:
-15918
请输入第二个值:
26214
转换结果:-26.3
  • 最简单方式,使用ByteArrayInputStreamDataInputStream

    public static float toFloat(byte[] bytes) throws IOException {
        ByteArrayInputStream mByteArrayInputStream = new ByteArrayInputStream(bytes);
        DataInputStream mDataInputStream = new DataInputStream(mByteArrayInputStream);
        try {
            return mDataInputStream.readFloat();
        } finally {
            mDataInputStream.close();
            mByteArrayInputStream.close();
        }
    }
    

测试结果:


    try {
            byte[] vs = new byte[]{0x41, (byte)0xd2, (byte)0x66, (byte)0x66};
            System.out.println(toFloat(vs));
            byte[] vs2 = new byte[]{(byte)0xC1, (byte)0xd2, (byte)0x66, (byte)0x66};
            System.out.println(toFloat(vs2));
        } catch (IOException e) {
            e.printStackTrace();
        }

> Task :Test.main()
26.3
-26.3

这两种实现方式可根据需求自行调用。
非常感谢你能看到这里,如果能够帮助到你是我的荣幸!

  • 36
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值