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

Posted Steven Jon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Modbus协议4个byte转成单精度float类型数据原理解析和基于Java的两种方式实现相关的知识,希望对你有一定的参考价值。

前言

学习过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

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

以上是关于Modbus协议4个byte转成单精度float类型数据原理解析和基于Java的两种方式实现的主要内容,如果未能解决你的问题,请参考以下文章

将python中的一个float变量转成内存的4个字节值

C语言当中int,float,double,char这四个有什么区别?

jdk源码解析--基本数据类型

java基本数据类型

Java数据类型转换变量和常量

基本类型与封装类