是否有可能删除浮点错误,而不诉诸任意的精度数据类型? [英] Is it possible to remove floating point errors without resorting to arbitrary-precision datatypes?

查看:145
本文介绍了是否有可能删除浮点错误,而不诉诸任意的精度数据类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道在特定条件下是否可以在不诉诸任意精度数据类型的情况下删除浮点错误。



问题是通常的问题。语言是Ruby,但它可以用任何语言:

  f = 1829.82 
=> 1829.82

f / 12.0
=> 152.485

(f / 12.0).round(2)
=> 152.48

为什么不是152.49?由于浮点数的有限精度:

  format(%18.14f,f)
=> ; 1829.81999999999994

格式(%18.14f,f / 12.0)
=> 152.48499999999999

所以舍入是正确的。现在我的问题是:有什么方法可以得到我想要的答案吗?在以下情况下:使用float执行的操作的数量有很大的限制,所需的精度限制在小数点后两位(最多8位数总共)和一小部分剩余的'错误'四舍五入的答案是可以接受的?



情况是这样的,用户可以输入有效的Ruby字符串,如:

 foo / 12.0

其中foo是在执行字符串的上下文中提供的一个数字,但是其中'12.0'是用户输入的内容。想象一下有一些免费公式字段的电子表格。字符串被简单地评估为Ruby,所以12.0变成了一个Float。我可以使用ruby_parser + ruby​​2ruby创建一个分析树,将数据类型转换为Bignum,Rational,Flt库中的某些东西,十进制浮点表示或者什么东西,但这很棘手,因为实际的字符串可能会变成有点复杂,所以我不喜欢这样。如果没有别的可能,我会这样做,但是这个问题是专门在这里,看看我能否避开这条路。因此,12.0的数据类型严格的是Float,结果是严格的Float,我唯一能做的就是解释片段的最终答案,并试图纠正它,如果它绕过错误的方式。 / p>

用户所做的唯一计算就是精确到两位十进制数(最多8位数)。用简单的意思是说浮点错误没有机会累加:我可以将这两个数字相加并除以一个整数,然后计算完成,结果被舍入并存储,随后的计算基于这个舍入的数字。通常只有一个浮点错误会被涉及,但是我认为这个问题不会显着改变,如果两个可以累积的话,尽管剩余错误率可能会比定义更大。



可能首先想到的是先舍入到3个十进制数字,然后是2.然而,这是行不通的。这将导致

  152.48499999999999 => 152.485 => 152.49 

还有

  152.4846 => 152.485 => 152.49 

这不是你想要的。

<接下来我想到的是,如果 边境。我主要想知道多久可能会导致一个误报:一个最小增量被添加的数字,即使它刚好低于.5边界的事实并不是由于浮点错误,而是因为它只是计算的结果?



第二个选项是:只要总是将最小增量加到数字上,就像.5

编辑:
我刚刚重写了这个问题,在cdiggins的建议中加入了我的答案的部分内容。我把这个奖赏授予了伊拉克·巴克斯特(Ira Baxter),因为他积极参与了讨论,尽管我还不确信他是对的:马克·兰森(Mark Ransom)和埃米利奥·布玛莎(Emilio M Bumachar)似乎支持我的观点,即修正是可能的,相对大多数情况下,产生正确的结果。


我仍然必须进行实验,看看结果是否正确,我完全打算这样做,但我花在这上面的时间有限,所以我还没有理解。这个实验并不简单。

解决方案

听起来像你想要的是固定精度的十进制数。一个好的库实现这些将比自己一起黑客攻击更可靠。



对于Ruby,请查看 Flt库


I was wondering whether, under specific conditions, it is possible to remove floating point errors without resorting to arbitrary-precision datatypes.

The problem is the usual one. The language is Ruby, but it holds in any language:

f = 1829.82
=> 1829.82

f / 12.0
=> 152.485

(f / 12.0).round(2)
=> 152.48

Why not 152.49? Because due to the finite precision of floating points:

format("%18.14f", f)
=> "1829.81999999999994"

format("%18.14f", f / 12.0)
=> "152.48499999999999"

So the rounding is correct. Now my question is: is there a way to get the answer I want anyway, given the following circumstances: there are strong limits on the (number of) operations performed using float, the precision needed is limited to two decimal places (max 8 digits in total) and a small amount of remaining 'wrongly' rounded answers is acceptable?

The situation is such that users may enter valid Ruby strings like:

"foo / 12.0"

where foo is a number provided in the context in which the string is executed, but where '12.0' is something the user enters. Imagine a spreadsheet with some free formula fields. The strings are simply evaluated as Ruby, so 12.0 becomes a Float. I could use the ruby_parser + ruby2ruby gems to build a parse tree, mangle the datatype to Bignum, Rational, something from the Flt library, decimal floating point representations or what-have-you, but that is tricky, as the actual strings can become somewhat more complex, so I prefer not to go this way. I will go that way if nothing else is possible, but this question is specifically here to see if I can avoid that path. As such, the datatype of the 12.0 is strictly Float and the outcome is strictly Float and the only thing I can do is interpret the final answer of the snippet and attempt to 'correct' it, if it rounds the 'wrong' way.

The only calculations the users do involve numbers with a precision of two decimal digits (and at most 8 digits in total). With 'simple' I mean that the floating point errors do not get a chance to accumulate: I may add two of these numbers and divide one by an integer, but then the calculation is done, the result is rounded and stored and any subsequent calculation is based on that rounded number. Usually only one floating point error will be involved, but I think the problem does not significantly alter if two can accumulate, though the residual error rate may be larger by definition.

What may first come to mind is first rounding to 3 decimal digits, then to 2. However, that doesn't work. That would lead to

152.48499999999999 => 152.485 => 152.49

but also

152.4846 => 152.485 => 152.49

which is not what you want.

What next came to my mind is adding the smallest possible increment (which, as people have pointed out, depends on the floating point value under consideration) to a float if that nudges it over the .5 border. I'm mainly wondering how often that could result in a 'false positive': a number to which the smallest increment is added, even though the fact that it was just below the .5 border was not due to a floating point error, but because it was simply the result of the calculation?

A second option is: just always add the smallest increment to numbers, as the .5 region is the only one where it matters anyway.

Edit: I just rewrote the question to incorporate part of my answers in comments, as cdiggins suggested. I awarded the bounty to Ira Baxter for his active participation in the discussion, though I'm not yet convinced he is right: Mark Ransom and Emilio M Bumachar seem to support my idea that a correction is possible that will, in practice, in possibly a relatively large majority of cases, produce the 'correct' result.

I still have to perform the experiment to see how often the result would be correct and I fully intend to, but the time I can spend on this is somewhat limited, so I haven't gotten round to it yet. The experiment is not trivial.

解决方案

It sounds like what you want are fixed-precision decimal numbers. A good library implementing these is going to be more reliable than hacking something together yourself.

For Ruby, check out the Flt library.

这篇关于是否有可能删除浮点错误,而不诉诸任意的精度数据类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆