我有一堆浮点数(Java双打),其中大部分都非常接近1,我需要将它们相乘作为更大计算的一部分.我需要做很多事情.
问题是虽然Java双打没有问题,例如:
0.0000000000000000000000000000000001 (1.0E-34)
它们不能代表以下内容:
1.0000000000000000000000000000000001
因此,我迅速失去了精确度(对于Java的双打,限制似乎约为1.000000000000001).
我考虑过只存储减去1的数字,所以例如1.0001将被存储为0.0001 - 但问题是再次将它们相乘我必须加1并且此时我失去了精度.
为了解决这个问题,我可以使用BigDecimals来执行计算(转换为BigDecimal,添加1.0,然后相乘),然后转换回双精度,但我对此的性能影响有严重的担忧.
任何人都可以看到一种避免使用BigDecimal的方法吗?
为清晰起见进行编辑:这是针对大规模协同过滤器,采用梯度下降优化算法.准确性是一个问题,因为协作过滤器通常处理非常小的数字(例如,人们点击产品广告的概率,可能是1000分之一,或10000分之一).
速度是一个问题,因为协作过滤器必须在数千万个数据点上进行训练,如果不是更多的话.
是的,因为
(1 + x) * (1 + y) = 1 + x + y + x*y
在你的情况,x
并且y
非常小,所以x*y
将是远较小-路太小,影响你的计算结果.所以就你而言,
(1 + x) * (1 + y) = 1 + x + y
这意味着您可以存储减去1的数字,而不是相乘,只需将它们相加即可.只要结果总是远小于1,它们就足够接近数学上精确的结果,你不会关心差异.
编辑:刚刚注意到:你说他们中的大多数非常接近1.显然,这种技术不适用于不接近1的数字 - 即,如果x
并且y
很大.但如果一个很大而一个很小,它可能仍然有效; 你只关心产品的大小x*y
.(如果两个数字都不接近1,你可以使用常规的Java double
乘法......)
也许你可以使用对数?
对数方便地减少乘法到加法.
另外,为了处理初始精度损失,还有函数log1p(至少它存在于C/C++中),它返回log(1 + x)而没有任何精度损失.(例如log1p(1e-30)为我返回1e-30)
然后你可以使用expm1来获得实际结果的小数部分.