sqlite – iOS上的TDateTime舍入错误
从时间戳(TDateTime)计算32位ID时,我得到一个奇怪的错误.在某些情况下,不同处理器的值不同.
从SQLite数据库中的Double字段读取fTimeStamp字段. ... 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有缺陷. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |