在sqlite中,有一个字段存储的是64位无符号整数。
小于 2^63-1 的数据都没问题,但是我们有大量超过此值的数据,比如 9223379502689557271,存到数据库里就变成了 -3.68861248304756E-311。
9223379502689557271 是 0x800006ca466e1b17,-3.68861248304756E-311 是一个精度不足(有效数字不足)的 0x800006ca466e1b17,我用数据库软件打开了这个数据库后,发现该浮点数的 hex 值依然是正确的,如图:(软件的十六进制展示是从右往左的)
我尝试着用 sqlite3_column_blob sqlite3_column_int sqlite3_column_text 等方法去读取这个值,但是得到的数据都是 -3.68861248304756E-311,而且,这个值因为精度不足,无法还原成 9223379502689557271,我想请问大神们,既然数据库存储的原始数据是准确的,那么我该如何书写代码,才能将 9223379502689557271 或者 0x800006ca466e1b17 准确的读取出来呢?
你们信么,我误打误撞把问题解决了!
sqlite3_column_value 可以直接获取二进制数据,我直接从地址里获取数据了,获取到了如图所示的数据:(部分字节做了一下 & 255 操作)
根据右边那一列标记来确定数据的位置,整数就在24-31字节,“浮点数”就在16-23字节,此问题完美解决,也谢谢各位回答的朋友了。
代码奉上:
char *key = NULL;
int i = 0;
key = sqlite3_column_value(stat, k);
if ((key[38] & 255) == 2) {
for (i = 23; i >= 16; --i) {
output = key[i] & 255;
if (output > 15) {
printf("%x", output);
}
else {
printf("0%x", output);
}
}
}
else {
for (i = 31; i >= 24; --i) {
output = key[i] & 255;
if (output > 15) {
printf("%x ", output);
}
else {
printf("0%x ", output);
}
}
}
具体稳定性还有待观察,但是这是我目前能想到的唯一思路了。而且,对于 sqlite3_column_value 返回的数据结构我还尚不熟悉,熟悉后可能会有更高效的写法。
========== 2019-11-15 更新 ==========
sqlite3_column_value 获取到的数据会受到sqlite.c版本影响,之前的代码是基于3.7.9写的,后来我将sqlite更新到3.29.0后,数据的位置变成了0-7字节,不区分整数型与浮点数型了。但是如果字段值本身是null,该处内存会保留上一条记录的数据,这会引起一些bug。好在8号字节有个标记,4和8为正常,1为异常的null(上一条数据的值),做下兼容判断就好。
key = (char *) sqlite3_column_value(stmt, 1);
if ((key[8] & 255) == 1) {
// null,直接跳过
continue;
}
for (i = 7; i >= 0; --i) {
valueNum = key[i] & 255;
if (valueNum > 15) {
_snprintf(value, 32, "%x", valueNum);
}
else {
_snprintf(value, 32, "0%x", valueNum);
}
strcat(record, value);
}
这个问题我回答过
https://ask.csdn.net/questions/756903
看我的回答
之前也是你问的
是这样的,你不能用double存储,得用两个long或者用字符串来存储。
数据这么大,你为啥不用long long类型,也就是int64
可能是因为你想存无符号,但是系统只能按照有符号整数来处理。
读成这个负小数应该是数据库在查询的时候就已经将其按有符号整数处理了(这种情况下不管怎么改query读出来都是负小数),你可以先把它按负小数读出来,保证HEX能够获得,然后在程序中“转换”成无符号整数。
例如一楼说过的暴力指针,就是将有符号指针指向的HEX码当做无符号指针指向的HEX码来处理,这样再读出来的就是原无符号整数了。当然这是C/Java中的处理思想,php有没有类似的机制不清楚,你可以查查。
如果你能确保所有数据都在64位无符号整数范围内,可以考虑这种方法。不过还是推荐二楼的方法,或者直接用字符串(如果不涉及查询query语句中的加减乘除),毕竟值很大而且不确定以后会不会更大。