如何处理接近1的数字的乘法
-
23-08-2019 - |
题
我有一堆浮点数(Java双打),其中大多数非常接近1,我需要将它们乘以较大计算的一部分。我需要这样做 很多.
问题是,虽然Java Doubles对这样的数字没有问题:
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 ++中存在),它返回日志(1+X)而没有任何精度损失。 (例如,log1p(1e-30)为我返回1E-30)
然后,您可以使用ExpM1获取实际结果的小数部分。
这种情况不是确切的bigdecimal是为了什么?
编辑以添加:
“根据第二段段落,出于绩效原因,我希望避免避免进行大事。” - 理智
“过早优化是万事万物的根源” - 诺斯
实际上,有一个简单的解决方案来解决您的问题。您担心它可能还不够快,所以您想做一些复杂的事情 思考 会更快。诺斯语言有时会被过度使用,但这正是他警告的情况。以简单的方式写。测试它。个人资料。看看它是否太慢。如果是 然后 开始考虑使其更快的方法。在您知道有必要之前,不要添加所有这些额外的复杂,容易出现的代码。
根据数字的来源以及您的使用方式,您可能需要使用理性而不是浮子。不是所有情况下的正确答案,而是 是 正确的答案确实没有其他。
如果理性不适合,我会认可对数答案。
响应您的编辑:
如果您要处理代表低响应率的数字,请执行科学家的工作:
- 将它们表示为过剩 /赤字(标准化1.0部分)
- 扩展它们。考虑“百万零件”或任何合适的东西。
这将使您处理合理的计算数字。
值得注意的是,您正在测试硬件而不是Java的限制。 Java使用CPU中的64位浮点。
我建议您在您认为对您的速度不够快之前测试BigDecimal的性能。您仍然可以使用BigDecimal进行数万计算。
正如大卫指出的那样,您只需添加偏移即可。
(1 + x) *(1 + y)= 1 + x + y + x * y
但是,选择退出最后一个学期似乎有风险。不。例如,尝试以下操作:
x = 1e-8 y = 2e-6 z = 3e-7 w = 4e-5
什么是(1+x)(1+y)(1+Z)*(1+W)?以双重精度,我得到:
(1+x)(1+y)(1+Z)*(1+W)
ans =
1.00004231009302
但是,如果我们只执行简单的添加近似,请查看会发生什么。
1+(x+y+z+w)
ans =
1.00004231
我们失去了可能很重要的低订单位。这只是一个问题,如果产品中的某些差异至少是SQRT(EPS),其中EPS是您正在工作的精度。
反试试:
f = @(u,v)u + v + u*v;
结果= f(x,y);
结果= f(结果,z);
结果= f(结果,w);
1+结果
ans =
1.00004231009302
如您所见,这使我们回到了双重精度结果。实际上,它更准确,因为结果的内部值为4.23100930230249E-05。
如果您真的需要精度,即使它慢于双人,也必须使用诸如bigdecimal之类的东西。
如果您真的不需要精确度,也许可以选择David的答案。但是,即使您经常使用乘法,也可能是一些过早的优化,所以无论如何,BigDecimal可能是要走的方式
当您说“大多数非常接近1”时,到底有多少?
也许您可以在所有数字中都有1的隐式偏移,并且只需使用分数即可。