Java估计Double的表示错误

Java估计Double的表示错误,java,math,double,currency,epsilon,Java,Math,Double,Currency,Epsilon,偶尔我会看到一些舍入错误,这是由于将一些值舍入到下面两个示例中所示的值而导致的 // floor(number, precision) double balance = floor(0.7/0.1, 3) // = 6.999 double balance = floor(0.7*0.1, 3) // = 0.069 当然,问题是0.7/0.1和0.7*0.1不是正确的数字,这是由于表示错误[检查下面的注释] 一种解决方案是添加一个epsilon,以便在应用地板之前减少任何表示错误 dou

偶尔我会看到一些舍入错误,这是由于将一些值舍入到下面两个示例中所示的值而导致的

// floor(number, precision)

double balance = floor(0.7/0.1, 3) // = 6.999 
double balance = floor(0.7*0.1, 3) // = 0.069
当然,问题是0.7/0.1和0.7*0.1不是正确的数字,这是由于表示错误[检查下面的注释]

一种解决方案是添加一个epsilon,以便在应用地板之前减少任何表示错误

double balance = floor(0.7/0.1 + 1e-10, 3) // = 7.0
double balance = floor(0.7*0.1 + 1e-10, 3) // = 0.07
我应该使用什么样的ε来保证它在所有情况下都能工作?我觉得这个解决方案相当粗糙,除非我有一个选择正确ε的好策略,这可能取决于我处理的数字

例如,如果有一种方法可以获得错误的估计值,如表示-数字或至少是表示>数字的符号,这将有助于确定在应用地板之前我应该在哪个方向更正结果

double balance = floor(0.7/0.1 + 1e-10, 3) // = 7.0
double balance = floor(0.7*0.1 + 1e-10, 3) // = 0.07
您可以想到的任何其他解决方法都非常受欢迎

注意:我知道真正的问题是我使用了double,它有表示错误。请不要说像我应该将余额存储在长数学中这样的话。floor3931809L/0.080241D同样不稳定。我也尝试过使用BigDecimal,但是性能下降了很多,因为它是一个实时应用程序。另外,请注意,我不太关心随着时间的推移传播小错误,我做了很多类似于上面的计算,但我每次都从一个新的平衡数开始,在返回并重新开始之前,我可能会做其中的3个操作

编辑:为了说明这一点,这是我唯一做的操作,我在同一个天平上重复了3次。例如,我取一个美元余额,然后将其转换为卢布,然后再转换为日元,然后再转换为欧元,然后返回余额,并用新的余额编号从头开始,即除了这3个操作外,不会传播舍入误差。除了正数之外,这些值不受限制,即在[0,+inf范围内,精度始终低于8位小数,即0.00000001是我必须处理的最小余额

我应该使用什么样的ε来保证它在所有情况下都能工作

没有一个ε能保证在所有情况下都有效

如果对应用程序正在执行的计算进行数学分析,那么就有可能计算出一个适合您的ε值

但请注意,在多步骤计算中反复舍入错误是有危险的。你最终可能会得到错误的答案。这就是数学所说的

最后,扪心自问:如果仅仅进行基于ε的调整是合法的/安全的,为什么50多年后,典型的手持计算器仍然坚持1.0/3*3等于0.9999999

1-让我们澄清一下。你没有试图指定你的案例是什么。所以我假设你指的是所有可能的计算


2-实数和相应的浮点二进制表示(例如,双精度)之间的ε取决于数字的二进制大小,这使得分析变得复杂。

二进制浮点数字双精度具有53位精度或大约15.95位十进制数字

设r为实数,d为最接近r的双精度数

εr=| r-d |在0到r*2floorlog2r-53的范围内

如果必须处理0到N范围内的数字,则整个范围内的最大ε值大约为:

N*2floorlog2N-53

执行计算时,需要估计计算中所有步骤的累积误差。加法、乘法和除法相对安全。例如,乘法:

设r1=d1+e1,r2=d2+e2

r1*r2=d1+e1*d2+e2=d1*d2+d2*e1+d1*e2+e1*e2

除非ε值已经很大,否则e1*e2项相对于其他项消失,ε将是≤ 2*max | d1 |,d2 |,*max | e1 |,e2 |

我想。我已经很久很久没有在生气的时候做这些了

然而,减法有令人讨厌的性质;参见Goldberg论文的定理9

您使用的楼层功能也有点棘手

设r=d+e

ε地板=|地板,3-地板,3|


那是≤ max | ceilinge,3 |,10-3

如果您感兴趣的只是将值转换回整数,那么使用它有什么问题?它将对0.5以上的所有值进行取整,对0.5以下的所有值进行取整,这为小错误提供了很大的余地。@SergGr我认为他不仅仅是在转换整数。@LAD,可能情况是,它不仅仅是一个整数关于转换为整数,但现在每个示例都显示转换为long,然后除以1000.0,我假设在本文中这是一个常数,表示所需的精度。@Serg
Gr-Hm,是的,如果变量被初始化为double,那就没什么意义了。@SergGr我没有把值转换回整数,你为什么得出这样的结论?long只影响分子,然后我有一个/1000.0,它将它转换回一个double-ie,cast只用于截断数字。这是一种简化,实际上我传递的是一个精度,它被转换成10^p的乘数,用于地板,但这不重要。我希望不是你否决了这个问题。干杯:我知道对于所有可能的双精度计算都没有银弹ε,但我的问题比一般情况更具体。在这种情况下,即给定舍入函数下限、平衡、速率和精度,可能有一种方法可以找到在所有情况下都能正常工作的ε。我只需要在同一天平上做几次这个操作,我认为地板引入的误差远远大于使用双打引入的任何舍入误差。你的问题可能更具体,但你没有告诉我们它是什么。除非你能清楚/准确/准确地告诉我们它是什么,否则我们无法进行分析,以确定1你的方法是否有效,2ε应该是什么。以何种方式不具体?让我知道我可以添加什么信息,我会编辑这个问题。我只需要在同一个天平上做几次这个操作,我认为地板引入的误差远远大于使用双精度的任何舍入误差。-这还不够详细。我们需要精确的操作顺序,包括几次的含义和涉及的数字范围。这是我们在这里讨论的正确数学,不是一些我认为错误。。。东西我认为这基本上是猜测,我在原始问题中有这些信息。操作总是平衡=地板平衡*速率、精度和我应用它的次数通常是3次,也可能是1或2次,但不会更多。换句话说,你有一个美元的余额,你把它转换成卢布一次操作,然后你得到卢布的余额,你把它转换成日元第二次操作,然后你得到日元的余额,你把它转换成欧元第三次操作。对于某些货币,我需要一个很大的精度,我使用8位小数。除正数外,所有其他值均不受约束。