Knowledge Base Nr: 00227 s5gleitpunkt.cpp - http://www.swe-kaiser.de

Downloads:

S5 Gleitpunktzahlen nach IEEE

  
//das float-format der S5 ist anderes als bei S7 und PC
//der code wurde aus einem Siemens S7 Codesample AWL nach C übersetzt.

unsigned long CS7Koppel::DoZweierkomplement(unsigned long dwIn)
{
//Eine Negation in diesem Zahlen-System entspricht einer bitweisen Invertierung,
//gefolgt von einer Addition von 1, wobei jeder an der Stelle n entstehende Übertrag ignoriert wird.
//Eine einfache Papier-und-Bleistift-Methode für die Negation übernimmt rechts beginnend jedes Bit
//bis einschließlich der ersten auftretenden 1 und invertiert ab dieser Position alle nachfolgenden Bits.
unsigned long dwOut = 0;
bool bInvert = false;

for (int b=0; b<32; b++)
{
if (bInvert)
dwOut |= (dwIn&(1<<b)) ? 0 : (1<<b);
else
dwOut |= dwIn&(1<<b);

if (dwIn&(1<<b))
bInvert = true;
}

return dwOut;
}

float CS7Koppel::S5toIEEE(unsigned char b1, unsigned char b2, unsigned char b3, unsigned char b4, bool* bError)
{
DEBUGOUT("S5toIEEE() %02x %02x %02x %02x", b1, b2, b3, b4);
*bError = false;

unsigned int nTmpData = 0;
nTmpData |= (((int)b1<<24) & 0xFF000000);
nTmpData |= (((int)b2<<16) & 0x00FF0000);
nTmpData |= (((int)b3<<8) & 0x0000FF00);
nTmpData |= (((int)b4<<0) & 0x000000FF);
DEBUGOUT("S5toIEEE() 0x%08x", nTmpData);

/////////////////////

const float MAX_FLOAT = 1.701412e+38;

bool bNeg = false;
bool bS5Mbit = false;

unsigned long dwS5temp = nTmpData;
unsigned long dwS7temp = 0;

unsigned long dw1Byte = (dwS5temp >> 24) & 0x000000FF;
unsigned long dw2Byte = (dwS5temp >> 16) & 0x000000FF;

if ((dw1Byte == 0x00000081) || (dw1Byte == 0x00000082))
{
*bError = true;
LOG("ERROR: S5toIEEE(0x%08X) ausser Bereich!\n", nTmpData);
return MAX_FLOAT;
}

if (dw2Byte & 0x80)
{
bNeg = true;
dwS7temp = DoZweierkomplement(dwS5temp);
}
else
{
dwS7temp = dwS5temp;
}

dw1Byte = (dwS5temp >> 24) & 0x000000FF;
dw2Byte = (dwS7temp >> 16) & 0x000000FF;
if (dw2Byte & 0x40)
{
bS5Mbit = true;
}

dwS7temp <<= 1;
dwS7temp &= 0x007FFFFF;

unsigned long dwExponent = 0;

bool bNorm = ((dwS7temp != 0) || (bS5Mbit == true));
DEBUGOUT("S5toIEEE() dwS7temp=0x%08x bS5Mbit=%d bNorm=%d", dwS7temp, bS5Mbit, bNorm);

if (!bNorm)
{
DEBUGOUT("S5toIEEE() dw1Byte=0x%08x bNeg=%d", dw1Byte, bNeg);
if (dw1Byte == 0x80)
return 0.0;
else if ((dw1Byte == 0x00) && !bNeg)
return 0.0;
else
dwExponent = dw1Byte + 127;
}
else //norm
{
DEBUGOUT("S5toIEEE() norm: dw1Byte=0x%08x", dw1Byte);
if (dw1Byte == 0x80)
{
*bError = true;
LOG("ERROR: S5toIEEE(0x%08X) zu klein!\n", nTmpData);
return MAX_FLOAT;
}
else
{
dwExponent = dw1Byte + 126;
}
}

//gpt:
dwExponent <<= 7;
dwExponent &= 0xFF80;

dwExponent <<= 16;
dwS7temp += dwExponent;

if (bNeg)
dwS7temp |= 0x80000000;
else
dwS7temp &= ~0x80000000;

float fData = 0.0;
memcpy(&fData, &dwS7temp, 4);
DEBUGOUT("S5toIEEE() fData=%g", fData);
return fData;
}

unsigned long CS7Koppel::IEEEtoS5(float fData, bool* bError)
{
*bError = false;

unsigned int nTmpData = 0;
memcpy(&nTmpData, &fData, 4);

/////////////////////

bool bNeg = false;

unsigned long dwS7temp = nTmpData;
unsigned long dwS5temp = 0;

if (fData >= 1.701412e+38)
{
*bError = true;
LOG("ERROR: IEEEtoS5(%g) >= 1.701412e+38\n", fData);
return ~0;
}

if (fData <= -1.701412e+38)
{
*bError = true;
LOG("ERROR: IEEEtoS5(%g) <= -1.701412e+38\n", fData);
return ~0;
}

if (fData == 0.0)
return 0x80000000; //s5:0

if (dwS7temp & 0x80000000)
bNeg = true;

dwS7temp <<= 1;

unsigned long dw1Byte = (dwS7temp >> 24) & 0x000000FF;

dwS5temp = (dw1Byte - 126) << 24;

dwS7temp |= 0x01000000;
dwS7temp &= ~0x02000000;
dwS7temp >>= 2;

if (bNeg)
dwS7temp = DoZweierkomplement(dwS7temp);

dwS7temp &= 0x00FFFFFF;
dwS7temp += dwS5temp;
return dwS7temp;
}