【问题标题】:How to efficiently serialize 64-bit floats so that the byte arrays preserve natural numeric order?如何有效地序列化 64 位浮点数以使字节数组保持自然数字顺序?
【发布时间】:2012-10-17 10:49:57
【问题描述】:

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

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

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

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

【问题讨论】:

  • 如何将 JavaScript 链接到数据库?在那个层做转换不是更有意义吗?
  • 如果您知道上述任何一种语言的答案,我也会接受 :)
  • 为什么是 BLOB?为什么不直接使用 FP 值作为主键呢?为什么不能使用原生 DB 类型?
  • 嗯,这是我正在从事的项目的不可协商的要求

标签: javascript python c serialization natural-sort


【解决方案1】:

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

  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);
}

【讨论】:

  • 好答案,我已对其进行编辑以添加 javascript 版本以供将来参考
  • javascript 解决方案无法序列化和反序列化NaN 和-0。它正确处理+0。我没有看到NaN 有任何好的排序,但我认为需要正确的序列化/反序列化 :)
  • @jonasfj 谢谢。现在看起来正确吗?此外,Python 版本在错误的位置排序 -0。
【解决方案2】:

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

使用 getFloat64() 和 setFloat64() 方法

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

https://github.com/vjeux/jDataView

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

【讨论】:

  • 我所要求的只是一个 javascript 函数 :) 顺便说一下,这个函数将在服务器上使用,而不是在浏览器中使用
  • 除非这满足订购要求,但它似乎并不满足,这不是问题的答案。
  • @EJP:怎么会?在大端格式中,双打按字典顺序排列。
  • @AkiSuihkonen:IEEE 浮点几乎是按字典顺序排列的,但不完全是。 Janne 的回答完成了交易。
  • 是的,加上位切换。但在代表任务最后 1% 的 javascript 中......
【解决方案3】:

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

【讨论】:

  • 我多么希望我可以轻松更改我从事的所有项目的要求 :)
  • 实际上有很多有效的用例。例如 Azure 表存储、AWS DynamoDB、AWS S3 中的密钥,可能还有大量其他 NoSQL 解决方案。它们通常只有字符串作为键,但按字典顺序排列......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 1970-01-01
  • 2014-01-27
  • 1970-01-01
  • 2010-09-15
  • 2014-01-31
  • 2017-07-24
相关资源
最近更新 更多