如何将负的二进制数转换为整数?

6
我想通过Node-RED的Modbus节点从数据源读取数据。范围是-20000到20000,但该节点无法处理负数,因此我不得不将它们转换为二进制数(DWORD),将它们分成低位和高位,并将这些单词转换回整数。
var low

function dec2bin(dec){
    return (dec >>> 0).toString(2);
}

var a = msg.payload

if (a >= 0){

    a = dec2bin(a);
    a = parseInt(a,2);

} else {

    a = dec2bin(a);
    a = a.substr(16);
    a = parseInt(a,2);

} 

low = { payload: a };

return low;

为了可视化,我想使用仪表板节点,但我需要将两个二进制字符串连接起来并将它们转换为整数。

问题:

Node-RED将它们转换为Qword,因此二进制数字1111 1111 1111 1111 1111 1100 0001 1000被视为4294966296,而不是-1000。但如果我将下一个剩余部分用1填充:1111 1111 1111 1111 1111 1111 1111 1111 1111 1100 0001 1000,输出结果为18446744073709552000。

谢谢

3个回答

4

parseInt函数适用于可变长度字符串作为输入,这意味着它不能将最高有效位识别为符号位。换句话说:它会像解析十进制字符串一样解析二进制字符串,并使用减号作为符号;因此,您必须使用不同的方法进行转换而不是parseInt

那么,有什么阻碍你直接将16位值作为无符号值进行传递吗?您只需检查传入javascript的值是否大于32767,然后手动执行二进制补码转换:

那么您的输入数字范围将为0..20000(表示正数“原始值”)和45536..65535(表示原始范围-20000..-1,如果我现在考虑正确的话,我没有验证)。这意味着转换很容易:

if( number > 32767 ) {
  number = -65536 + number;
}

或者,对于任何二进制数字类型(小于您所使用的语言的数字大小):

if( number > SIGNED_MAX_INT ) {
  number = -(UNSIGNED_MAX_INT+1) + number;
}

(这些常量并不存在于所写的代码中,它们更像是伪代码)


2
首先,在函数节点中创建新的msg对象是非常不好的做法,你应该只更新msg.payload值并传递原始对象。
其次,最简单的方法是使用缓冲区。
例如:
var b = Buffer(4)
b.writeUInt32BE(msg.playload)
msg.payload = b.readInt32BE()

return msg;

手动模式下的连接节点可以将两个较小的缓冲区合并成一个长度为16位的缓冲区。


谢谢你的回答,你能告诉我为什么创建新对象是一种不好的做法吗?问题是,我有两个uint16值。我该如何将缓冲区合并为一个,以便我可以使用你的函数? - c0nr3f
这是高位的例子: {"data":[65535],"buffer":[0xff,0xff]},低位则为: {"data":[64536],"buffer":[0xfc,0x18]}。 - c0nr3f
好的,既然已经是缓冲区了,只需将它们合并成一个单一的缓冲区,并使用提供的方法从中读取即可。您不需要创建新的消息,因为这会丢弃可能随有效负载一起传输的任何额外元数据,例如由 http-in 节点添加的 msg.resp 对象,该对象由 http-response 节点使用。 - hardillb
我无法使用连接节点将它们合并,我是否需要手动完成,并且您能告诉我如何操作吗?抱歉,我对JavaScript还很陌生。 - c0nr3f
不行,但我可以将msg.complete设置为至少在第二个电报中排列顺序。如果我手动加入作为缓冲区,就会出现错误。但如果我作为数组加入,我会得到0的返回值。加入函数后的示例:[{"data":[64536],"buffer":[252,24]},{"data":[65535],"buffer":[255,255]}] - c0nr3f
显示剩余2条评论

1

目前来说,JavaScript因为没有强类型而有些局限。虽然不是最好的方法,但可以创建自定义的Int类来实现此功能。

class Int {
  constructor(length, binaryStr) {
    const cleanBinary = "0".repeat(length - binaryStr.length) + binaryStr
    if (cleanBinary.startsWith("1")) {
      const invertedBinary = cleanBinary.split("")
        .map(char => char === "1" ? "0": "1")
        .join("")
      this.decValue = -parseInt(invertedBinary, 2) - 1
    } else {
      this.decValue = parseInt(cleanBinary, 2)
    }
  }
}

这样类似的东西

new Int(8, "11111100")
> Int {decValue: -4}

strBin = "11111111111111111111110000011000";
new Int(strBin.length, strBin)
> Int {decValue: -1000}

这可能会进一步发展成为更高级的内容,并且也可以在浏览器中使用。

我在将这个类实现到Node-RED中遇到了一些问题。此外,我对JavaScript还比较陌生,所以将其转换为函数对我来说并不容易。我需要的是:strBin = msg.payload; new Int(strBin.length, strBin) msg.payload = Int; 但是由于它是一个类,所以这样做行不通。你有什么建议吗? - c0nr3f
如果您有一个固定大小的数字,例如16位,您可以通过以下方式获取答案:const convertedNumber = new Int(16, msg.playload).decValue。但请注意,该类只接受没有空格的二进制。您可以在之前添加一些格式。 - Tim Nimets

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接