现在的位置: 首页 > 综合 > 正文

D3D修改浮点数精度

2013年05月09日 ⁄ 综合 ⁄ 共 1640字 ⁄ 字号 评论关闭

前几天策划要求给任务系统加上日常任务的功能,这个需求很简单:象
wow
一样,日常任务在每天某个时候定时刷新。在思考添加这个功能时,很直接想到的方案是服务器取出
3
个时间:任务上次完成时间(
pre
),当前时间(
now
)和当天刷新时间(
refresh
),然后经过一些较复杂的逻辑完成(此处省略
xx
字)。由于该方案需要涉及一些时间操作,而在
lua

c++
里面做日期时间的操作又是比较麻烦的一件事。水古给出了一个解法,很好的解决了这个问题:首先用当前时间(比如现在是
18:00
)减去当天刷新时间(比如
wow
的日常每天
15:00
刷新),这样就把时间对齐到前一天的
0
点,然后分别用当前时间和上次完成时间除以一天的秒数(
24*60*60
),就分别得到了当前时间和上次完成时间的天数,如果上次完成时间的天数小于当前时间的天数,则日常任务刷新。
lua
代码大致如下:

local now = os.time()

local pre = task.completed_time

local refresh = (15-8)*(60*60)  
--


8
是因为
os.time()
得到的是
UTC
时间,而北京时间是
UTC+8

local pre_days = (pre-refresh)/(24*60*60)

local now_days = (now-refresh)/(24*60*60)

if(now_days > pre_days) then

      
--

刷新任务

end

很简洁,整个算法只需要一个时间函数——
os.time()

 

本以为这个功能就这样完成了,但经过反复测试,悲剧发生了:服务器的时间正确,客户端却有
1
分钟左右的偏差。同样的库,同样的脚本,却得到不同的时间。掘地三尺后发现在客户端中
lua
中的双精度浮点数经过加减运算后变成了单精度浮点数,由于精度问题导致产生一定的时间偏差——和谐的阳光照在杯具上,每一个杯具都笑开颜。

首先想到的还是
lua
库的问题,毕竟是在
lua
脚本里面精度变了,但我直接在
c++
里面写了一个简单的测试,发现精度也改变了。再次和几个朋友讨论这个问题,水古提到:
DX
会改变浮点精度,确实,我记忆中好像在哪看到过,搜索
D3D sdk
,在
CreateDevice
中发现
D3DCREATE
有这么一个选项

D3DCREATE_FPU_PRESERVE

Set the precision for Direct3D floating-point calculations
to the precision used by the calling thread. If you do not specify this flag,
Direct3D defaults to single-precision round-to-nearest mode for two reasons:

  • Double-precision
    mode will reduce Direct3D performance.
  • Portions of
    Direct3D assume floating-point unit exceptions are masked; unmasking
    these exceptions may result in undefined behavior.

很明显了,
d3d
在未指定该参数的时候会把
FPU
设置为单精度模式,其原因是因为处理单精度数据比双精度具备更好的性能。由于
D3D
默认修改了
FPU
,影响到了
lua
的精度(
lua
里面
number
只有
double
),造成了一定时间的误差。

至于性能问题,我是这样想的,以单个浮点运算来看,现代
cpu

double

float
的运算效率非常接近了,更多的效率考虑可能是
cpu
高速缓存的命中和内存带宽的暂用吧,毕竟
double

float
多一倍的数据量,而在
3D
环境中浮点运算大量存在。

 

最后说一下,该问题只是一个中间过程,最后把客户端时间修改成服务器的时间,在客户端就不用很精确的时间控制,所以这里暂时无需修改客户端代码,客户端仍然以默认的
24

FPU
精度模式运行,如何兼顾性能和精度,也就没去深究了。

 

 

抱歉!评论已关闭.