i've got a 32 bit unsigned "Real" value splitted into two 16 Bit signed "Word" (0-65535) values. How do I convert them to a javascript number?
Example:
Value1: 18584
Value2: 18081
The Real value is: 20644.3
I'm searching for a function like back2Real(18584, 18081) which returns 20644.3. The values come from a modbus application (NodeJS / modbus_stack). The SPS/modbus server sends a "Real" value splitted into two Word-registers.
Regards,
root66
You can use the new(ish) typed array functionality to simplify this.
function uint16ToFloat32(low, high) {
var buffer = new ArrayBuffer(4);
var intView = new Uint16Array(buffer);
var floatView = new Float32Array(buffer);
intView[0] = low;
intView[1] = high;
return floatView[0];
}
function float32ToUint16(value) {
var buffer = new ArrayBuffer(4);
var intView = new Uint16Array(buffer);
var floatView = new Float32Array(buffer);
floatView[0] = value;
return [intView[0], intView[1]];
}
console.log("Converted ints to", uint16ToFloat32(18584, 18081));
console.log("Converted float to", float32ToUint16(20644.297));
Here's a transcript:
$ node floatsplit.js
Converted ints to 20644.296875
Converted float to [ 18584, 18081 ]
$
Use this function to convert to JavaScript numbers. Since JavaScript uses double precision rather than single precision numbers, some rounding may occur.
function back2Real(low, high){
var fpnum=low|(high<<16)
var negative=(fpnum>>31)&1;
var exponent=(fpnum>>23)&0xFF
var mantissa=(fpnum&0x7FFFFF)
if(exponent==255){
if(mantissa!=0)return Number.NaN;
return (negative) ? Number.NEGATIVE_INFINITY :
Number.POSITIVE_INFINITY;
}
if(exponent==0)exponent++;
else mantissa|=0x800000;
exponent-=127
var ret=(mantissa*1.0/0x800000)*Math.pow(2,exponent)
if(negative)ret=-ret;
return ret;
}
The following function converts JavaScript numbers into 32-bit IEEE floating point numbers, split into the low and high word:
function real2Back(value){
if(isNaN(value))return [0,0xFFC0]
if(value==Number.POSITIVE_INFINITY || value>=3.402824e38)
return [0,0x7F80]
if(value==Number.NEGATIVE_INFINITY || value<=-3.402824e38)
return [0,0xFF80]
var negative=(value<0)
var p,x,mantissa
value=Math.abs(value)
if(value==2.0)return [0,0x4000]
else if(value>2.0){
// positive exponent
for(var i=128;i<255;i++){
p=Math.pow(2,i+1-127)
if(value<p){
x=Math.pow(2,i-127)
mantissa=Math.round((value*1.0/x)*8388608)
mantissa&=0x7FFFFF
value=mantissa|(i<<23)
if(negative)value|=(1<<31)
return [value&0xFFFF,(value>>16)&0xFFFF]
}
}
// return infinity
return negative ? [0,0xFF80] : [0,0x7F80]
} else {
for(var i=127;i>0;i--){
// negative exponent
p=Math.pow(2,i-127)
if(value>p){
x=p
mantissa=Math.round(value*8388608.0/x)
mantissa&=0x7FFFFF
value=mantissa|(i<<23)
if(negative)value|=(1<<31)
return [value&0xFFFF,(value>>16)&0xFFFF]
}
}
// subnormal
x=Math.pow(2,i-126)
mantissa=Math.round((value*8388608.0/x))
if(mantissa>0x7FFFFF)mantissa=0x800000
value=mantissa
if(negative)value|=(1<<31)
return [value&0xFFFF,(value>>16)&0xFFFF]
}
}
I hope this helps. The code is in the public domain.
Buffers normally arrays.
Slightly changed first function to accept array, not low and high.
It's important to say it's valid for Big Endian format without swap.
function uint16ToFloat32(uint16array) {
var buffer = new ArrayBuffer(4);
var intView = new Uint16Array(buffer);
var floatView = new Float32Array(buffer);
intView[0] = uint16array[0];
intView[1] = uint16array[1];
return floatView[0];
}