加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

sqlite – iOS上的TDateTime舍入错误

发布时间:2020-12-12 19:01:17 所属栏目:百科 来源:网络整理
导读:从时间戳(TDateTime)计算32位ID时,我得到一个奇怪的错误.在某些情况下,不同处理器的值不同. 从SQLite数据库中的Double字段读取fTimeStamp字段. 下面的代码计算来自fTimeStamp的32位ID(lIntStamp),但在某些(罕见)情况下,即使源数据库文件完全相同(即存储在文
从时间戳(TDateTime)计算32位ID时,我得到一个奇怪的错误.在某些情况下,不同处理器的值不同.

从SQLite数据库中的Double字段读取fTimeStamp字段.
下面的代码计算来自fTimeStamp的32位ID(lIntStamp),但在某些(罕见)情况下,即使源数据库文件完全相同(即存储在文件中的Double相同),不同计算机上的值也不同.

...
fTimeStamp: TDateTime
...

var
  lIntStamp: Int64;
begin
  lIntStamp := Round(fTimeStamp * 864000); //86400=24*60*60*10=steps of 1/10th second
  lIntStamp := lIntStamp and $FFFFFFFF;
  ...
end;

TateTime(Double)的精度是15位数,但代码中的舍入值仅使用11位数,因此应该有足够的信息正确舍入.

提一个值的例子:在特定的测试运行中,lIntStamp的价值在Windows计算机上为74AE6699B,在iPad上为74AE699A(=最后一位不同).

Round功能在每个平台上实现了不同吗?

PS.我们的目标平台目前是Windows,MacOS和iOS.

编辑:

我根据评论做了一个小测试程序:

var d: Double;
    id: int64 absolute d;
    lDouble: Double;
begin
  id := $40E4863E234B78FC;
  lDouble := d*864000;
  Label1.text := inttostr(Round(d*864000))+' '+floattostr(lDouble)+' '+inttostr(Round(lDouble));
end;

Windows上的输出是:

36317325723 36317325722.5 36317325722

在iPad上输出是:

36317325722 36317325722.5 36317325722

区别在于第一个数字,它显示了中间计算的舍入,因此问题发生是因为x86具有比ARM(64位)更高的内部精度(80位).

解决方法

假设所有处理器都符合IEEE754标准,并且您在所有处理器中使用相同的舍入模式,那么您将能够从所有不同的处理器获得相同的结果.

但是,可能存在编译的代码差异,或者与代码的实现差异.

考虑如何

fTimeStamp * 24 * 60 * 60 * 10

被评估.有些编译器可能会执行

fTimeStamp * 24

然后将中间结果存储在FP寄存器中.然后将其乘以60,并存储到FP寄存器.等等.

现在,在x86下,浮点寄存器是80位扩展的,默认情况下,这些中间寄存器将结果保持为80位.

另一方面,ARM处理器没有80个寄存器.中间值保持64位双精度.

这就是机器实现的差异,可以解释您观察到的行为.

另一种可能性是ARM编译器在表达式中发现常量并在编译时对其进行求值,将上面的值减少到

fTimeStamp * 864000

我从未见过这样做的x86或x64编译器,但也许是ARM编译器.这是编译代码的不同之处.我不是说它发生了,我不知道移动编译器.但没有理由不能发生这种情况.

但是,这是你的救赎.使用该单次乘法重写上面的表达式.这样你就可以摆脱存储到不同精度的中间值的任何范围.然后,只要Round在所有处理器上意味着相同的事情,结果将是相同的.

就个人而言,我会避免关于舍入模式的问题而不是Round将使用Trunc.我知道它有不同的含义,但为了你的目的,这是一个任意的选择.

然后你会离开:

lIntStamp := Trunc(fTimeStamp * 864000); //steps of 1/10th second
lIntStamp := lIntStamp and $FFFFFFFF;

如果Round在不同平台上的行为不同,那么您可能需要在ARM上自己实现它.在x86上,默认的舍入模式是银行家.这只有在两个整数之间的中间才有意义.因此,检查Frac(…)是否为0.5并相应地进行舍入.该检查是安全的,因为0.5是完全可表示的.

另一方面,你似乎在声称这一点

Round(36317325722.5000008) = 36317325722

在ARM上.如果是这样的话就是一个bug.我无法相信你的要求.我相信传递给Round的价值实际上是ARM上的36317325722.5.这是对我来说唯一有意义的事情.我不敢相信Round有缺陷.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读