float类型数据计算问题

  • 11 replies
  • 41459 views
float类型数据计算问题
« 于: 三月 12, 2019, 11:42:42 am »
内核函数中float变量(f)的计算结果(正数)是一个小数值,很小的一个值(但不能忽略),在进行 f>0.0f 比较操作时候被认为无法大于0而忽略下面操作,有什么比较好的解决办法?(数据类型float不变前提下)

Re: float类型数据计算问题
« 回复 #1 于: 三月 12, 2019, 12:11:22 pm »
CUDA的浮点计算是兼容IEEE754-2008规范的呢,不妨提供一下问题发生时的细节~

Re: float类型数据计算问题
« 回复 #2 于: 三月 12, 2019, 02:09:31 pm »
是这样的一个计算,关于点的位置更新:newX=old.x+v*t, 计算点之间的分布间距取大点小数没问题比如0.025,但在缩小几倍如0.00625,然后做内核计算,到上述关于位置更新那块完成之后,用if(newX>0.0f)判断总是不能执行,调试查明之后是newX这个值估计是偏小以至于忽略了(个人认为),换用double测试可以,但这个目前还不能这样改(程序设计原因),所以想先找找有没有其他的办法可以解决这个问题。

Re: float类型数据计算问题
« 回复 #3 于: 三月 12, 2019, 02:39:10 pm »
是这样的一个计算,关于点的位置更新:newX=old.x+v*t, 计算点之间的分布间距取大点小数没问题比如0.025,但在缩小几倍如0.00625,然后做内核计算,到上述关于位置更新那块完成之后,用if(newX>0.0f)判断总是不能执行,调试查明之后是newX这个值估计是偏小以至于忽略了(个人认为),换用double测试可以,但这个目前还不能这样改(程序设计原因),所以想先找找有没有其他的办法可以解决这个问题。

有2个疑问,这代码看上去是做某种例子模拟(假设的),根据你的上下文,应当不是直接对下一步的新位置进行判断才对,而是应当类似:
delta_pos > threshold
否则这种代码对于点的绝对位置和0位置对比,往往一般无意义。

此外,如果是位置差的对比的话,非常临近的两个点的对比可能没有好办法,因为float本身的有效数字才23位,很小的差值可能很容易被近似成0的。

而如果只是1个点的两次更新的位置前后对比的话(特别点的绝对坐标较大的时候,此时对点的位置更新会被忽略(大数吃掉小数,也依然是有效位数的问题)),但是此时可以直接比较更新值(即,x' = x + v * t中的v * t部分),虽然此时如果参与运算,会导致较大的x吃掉较小的更新(vt), 但是单独拿出来v * t使用还是无问题的,此时应当直接考虑v * t这个差值,和很小的数值(例如0)之间的避免,以规避这个问题。

具体楼主什么情况,欢迎更新。

Re: float类型数据计算问题
« 回复 #4 于: 三月 12, 2019, 02:42:46 pm »
有2个疑问,这代码看上去是做某种例子模拟(假设的),根据你的上下文,应当不是直接对下一步的新位置进行判断才对,而是应当类似:
delta_pos > threshold
否则这种代码对于点的绝对位置和0位置对比,往往一般无意义。

此外,如果是位置差的对比的话,非常临近的两个点的对比可能没有好办法,因为float本身的有效数字才23位,很小的差值可能很容易被近似成0的。

而如果只是1个点的两次更新的位置前后对比的话(特别点的绝对坐标较大的时候,此时对点的位置更新会被忽略(大数吃掉小数,也依然是有效位数的问题)),但是此时可以直接比较更新值(即,x' = x + v * t中的v * t部分),虽然此时如果参与运算,会导致较大的x吃掉较小的更新(vt), 但是单独拿出来v * t使用还是无问题的,此时应当直接考虑v * t这个差值,和很小的数值(例如0)之间的避免,以规避这个问题。

具体楼主什么情况,欢迎更新。

此外,见过以前的类似代码,楼主们写到这样:
acc = 0.0f;
....
x' = x + (v * t + acc);  //注意括号不能省略
if (x' == x) //更新丢失
{
    acc += v * t; //积攒到下次机会再尝试更新
}
else
{
    acc = 0.0f;
}

仅供参考。

Re: float类型数据计算问题
« 回复 #5 于: 三月 12, 2019, 02:54:57 pm »
是粒子模拟计算:每一步都更新粒子位置,在下一步粒子位置更新完后即newX值,用于0比较以便要进行该粒子的其他属性更新。但就是这个判断一直无法进行,所以觉得有问题。

Re: float类型数据计算问题
« 回复 #6 于: 三月 12, 2019, 03:15:18 pm »
是粒子模拟计算:每一步都更新粒子位置,在下一步粒子位置更新完后即newX值,用于0比较以便要进行该粒子的其他属性更新。但就是这个判断一直无法进行,所以觉得有问题。

如果不存在其他情况,不考虑异常值(inf, nan之类的), 单纯的就这个式子来说,如果是用位置绝对值和0判断的话,

那么如果在一步或者多步都始终陷入了0这里无法出来,则显然只能说是更新增量太小了,计算出来被认为是0了,
也就是你的v * dt太小,这又可以分成2种情况讨论:
(1)v太小(例如非常接近0),导致多次累加乘以dt依然非常接近0, 导致更新失败。这种情况建议考虑上double(你可能需要更换更好的卡),或者考虑累加多次dt一并更新,特别是当你的dt也非常接近0的情况(请考虑两个subnormal的数值,都含有各自的有效数字,然后相乘变成了0的情况)。
(2)v较大,或者v在某次变化后可能会变得较大,则应当只需要考虑是否你选择的dt过小。此时可以简单的通过调整步伐来解决。

请他情况欢迎补充。

Re: float类型数据计算问题
« 回复 #7 于: 三月 12, 2019, 03:49:19 pm »
是绝对位置更新值和0的比较,是多步循环下每次更新都这样,导致结果输出不对,我调试输出了下更新后的new值和v*t值,发现结果也不是很小,不应该被舍弃的,具体结果如附件显示。难道是我的程序问题》但问题是我detX取大点结果是可以判断并输出正确结果的,很纠结这个。。

Re: float类型数据计算问题
« 回复 #8 于: 三月 12, 2019, 03:55:21 pm »
0.00019f这种和0比较大小是肯定可以的,如果真是图中所说的数据(例如你通过调试器看到的),

则应当不是比较处的问题:
(23位存储的有效数字(二进制),等效于大约10进制的7位左右的有效数字,指数范围大约在(2^-127, 2^127]左右, 你的数据能用float表达)。不存在该数据无法得出if (0.00019f > 0.0f)的比较的可能。

应当考虑是否其他方面引入的其他原因(可能是另外一个话题,或者说其他方面的bug了)

Re: float类型数据计算问题
« 回复 #9 于: 三月 12, 2019, 03:57:33 pm »
0.00019f这种和0比较大小是肯定可以的,如果真是图中所说的数据(例如你通过调试器看到的),

则应当不是比较处的问题:
(23位存储的有效数字(二进制),等效于大约10进制的7位左右的有效数字,指数范围大约在(2^-127, 2^127]左右, 你的数据能用float表达)。不存在该数据无法得出if (0.00019f > 0.0f)的比较的可能。

应当考虑是否其他方面引入的其他原因(可能是另外一个话题,或者说其他方面的bug了)


一定要从调试器看值!例如你刚才的0.00019...., 不要用printf看!!你看下你从调试器,正常的代码中观察,看看运行的结果是具体多少!

Re: float类型数据计算问题
« 回复 #10 于: 三月 12, 2019, 04:05:56 pm »
好的,谢谢!我再调试看看,有什么问题在附贴说明。

Re: float类型数据计算问题
« 回复 #11 于: 三月 12, 2019, 04:14:14 pm »
好的。如果有其他方面的问题,也欢迎新开贴。