0 + 프로그래밍

[알고리즘] float32 부동소수점 계산 및 변환 (java, float32 to int, plc, modbus)

힘들면힘을내는쿼카 2021. 6. 20. 16:01
728x90
반응형

 

 

 

 

PLC 장비에서 modbus 프로토콜을 이용하여 데이터를 처리하다보면

float32형식의 데이터를 받는 경우가 생깁니다.

예를 들면

아래와 같이 modbus 프로토콜이 정의된 경우가 있습니다.

주소 내용 순번 데이터 형식
30001 A상 전압 Low float32
30002 High

이러한 데이터를 처리하면

30001번지에서 0x36BB, 30002번지에서 0x4358 으로 데이터를 받는 것을 확인 할 수 있습니다.

이때 A상전압을 0x36BB4358으로 처리하면...?

상전압이 918,242,136 ????????????????

 

 

 

이때 A상전압을 0x36BB4358으로 처리하게 되면 안됩니다.

순번에 맞게 30002번지가 앞16비트 30001번지가 뒤 16비트로 처리해야합니다.

float32

이제 부동소수점 방식을 이용하여 처리하면 됩니다.

 

 

부동소수점 계산 방법은 다음과 같습니다.

( 참조:  https://ko.wikipedia.org/wiki/%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90)

부호, 지수, 가수부

부동소수점은 부호, 지수, 가수 이렇게 3가지로 나뉘게 됩니다.

부호, 지수, 가수부를 분리해보겠습니다.

부호부는 데이터에 0x80000000를 and연산 한뒤 오른쪽으로 31비트 이동시킵니다.

지수부는 데이터를 0x7f800000를 and 연산한 뒤 오른쪽으로 23비트 이동시킵니다.

가수부는 데이터를 0x7fffff를 and 연산한 뒤 0x800000를 더해 줍니다.

이를 코드로 설명하면 다음과 같습니다.

sign = (Data & 0x800000) >> 31; //부호부
exponent = (Data& 0x7f800000) >> 23; //지수부
mantissa = (Data & 0x7fffff) + 0x800000; //가수부

부호부, 지수부, 가수부로 분해 한뒤 이제 계산을 해야합니다.

mantissa에 0x800000를 더한 이유는 Result는 정규화된 값을 의미하기 때문이다.

 

정규화

오른쪽 값과 같이 표현하는 것을 의미한다.

따라서 가수부에서 0x800000를 더한다.

 

반응형

 

그러면 계산식은 아래와 같다.

부동소수점 계산식

sign는 0

exponent는 134임을 알 수 있다.

이때 지수를 의미하는 exponent에서 127를 왜 빼지? 라는 의문을 갖을 수 있다.

지수를 표현하는 부분이 8bit이기 때문에 -128 ~ 127까지 표현할 수 있음을 알 수 있다. 따라서 지수부에서 -127을 하는 것이다. 이를 부동소수점의 bias라고 한다.

 

모든 내용을 java 코드로 표현하면 다음과 같다.

private void convertFloat32() {
    long sign = 0; //부호부
    long exponent = 0; //지수부
    long mantissa = 0; //가수부
    long sum = 0; //4byte
    double result = 0;//최종 결과
    int[] pdata = new int[2];//데이터
    float [] m = new float[23];
    pdata[1] = 0x4358; //High
    pdata[0] = 0x36bb; //Low
    pdata[1] = pdata[1] << 16;
    
    sum = pdata[0] + pdata[1];

    sign = (sum & 0x80000000) >> 31;
    exponent = (sum & 0x7f800000) >> 23;
    mantissa = (sum & 0x7fffff) + 0x800000;
    
    System.out.println("sign " + sign);
    System.out.println("exponent " + exponent);
    System.out.println("mantissa " + mantissa);
    
    int k = 23; //2의 n제곱을 총23비트인 가수부에 곱하기 위해 선언
    
    for(int i =0; i < 23; i++)
    {
        m[i] = (float)(((mantissa >> k) & 0x01) * Math.pow(2, exponent - 127 - i));
        result =  result + m[i];
        k--;
    }
    
	System.out.println("result "+(float) result); //결과 확인
}

 

 

 

728x90

 

java에서 제공하는 Float.floatToIntBits(float value); 함수를 사용해도 된당. 😎

@HotSpotIntrinsicCandidate
public static int floatToIntBits(float value) {
    if (!isNaN(value)) {
        return floatToRawIntBits(value);
    }
    return 0x7fc00000;
}
    
//===//

@HotSpotIntrinsicCandidate
public static native int floatToRawIntBits(float value);

 

 

 

728x90
반응형