3

我正在处理的项目要求我将 javascript 数字(它们是双精度数)作为 BLOB 主键存储在数据库表中(不能使用数据库本机浮点数据类型)。所以基本上我需要以这样的方式将数字序列化为字节数组:

1 - 字节数组的长度为 8(通常需要序列化双精度)

2 - 字节数组必须保持自然顺序,以便数据库透明地对索引 b 树中的行进行排序。

我正在寻找一个简单的函数,它接受一个数字并返回一个表示字节的数字数组。我更喜欢用 javascript 编写的函数,但也可以接受用 java、C、C#、C++ 或 python 编写的答案。

4

3 回答 3

5

要满足排序要求,您需要:

  1. 使用大端表示
  2. 如果符号位为 0,则将其翻转为 1。检查实际位 - 将原始数字与 0 进行比较在特殊情况下(例如负零)会得到不同的结果。
  3. 如果符号位为 1,则翻转所有位

Python 3 代码:

import struct

def invert(x):
    return bytes(c ^ 255 for c in x)

def tobin(x):
    binx = struct.pack('>d', x)
    if binx > b'\x80': #negative
        return invert(binx)
    else:
        return struct.pack('>d', -x)

data = [float('-inf'), -100.0, -2.0, -.9, -.1, -0.0, 
    0.0, .1, .9, 2.0, 100.0, float('inf'), float('nan')]

print(sorted(data, key=tobin))
#[-inf, -100.0, -2.0, -0.9, -0.1, -0.0, 0.0, 0.1, 0.9, 2.0, 100.0, inf, nan]

在 Python 2 上更改invert为:

def invert(x):
    return "".join(chr(ord(c) ^ 255) for c in x)

作为参考,这里是等效的 node.js,它已经通过 'Buffer' 类实现了 Big Endian 序列化功能:

function serialize(n) {
  var buffer = new Buffer(8);
  var l = buffer.length;
  buffer.writeDoubleBE(n, 0);
  if (buffer[0] < 0x80) {
    buffer[0] ^= 0x80;
  } else {
    for (var i = 0; i < l; i++) 
      buffer[i] ^= 0xff;
  }
  return buffer
}

function deserialize(buffer) {
  var l = buffer.length;
  // 0x80 is the most significant byte of the representation of
  // the first positive number(Number.MIN_VALUE)
  if (buffer[0] >= 0x80) { 
    buffer[0] ^= 0x80;
  } else {
    for (var i = 0; i < l; i++) 
      buffer[i] ^= 0xff;
  }
  return buffer.readDoubleBE(0);
}
于 2012-10-17T11:56:02.900 回答
0

DataView提供了一个在 8 字节容器中写入双精度的 API。

用方法getFloat64()setFloat64()

提到的链接显然没有实现 setFloat64(),但至少提供了一些观点,至少在编写这样一个函数时涉及到什么。

https://github.com/vjeux/jDataView

对于浏览器,绝对最好和最快的方法是使用类型数组中的 Float64Array(并可能修复字节顺序)。

于 2012-10-17T10:57:31.787 回答
0

显而易见的答案是消除您不能使用本机数据库类型的限制。我看不出有任何意义。它仍然是 8 个字节,无需任何进一步的调查、工作、实验、测试等即可为您进行排序。

于 2012-10-17T11:39:18.613 回答