Ruby BigDecimal完整性检查(浮点newb) [英] Ruby BigDecimal sanity check (floating point newb)

查看:228
本文介绍了Ruby BigDecimal完整性检查(浮点newb)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的理解是正确的,即使用Ruby BigDecimal 类型(即使精度和标度长度不同)应该准确地计算还是应该预测浮点运算符?



Rails应用程序中的所有值都是 BigDecimal 类型,我看到一些错误(它们有不同的小数),希望这只是我的方法而不是我的对象类型。

解决方案

使用浮点运算有两个常见的陷阱。 >

第一个问题是Ruby浮点具有固定的精度。在实践中,这或者是1)对你来说没有问题,或者2)灾难性的,或者3)中间的事情。考虑如下:

 #float 
1.0e + 25 - 9999999999999999900000000.0
#=> 0.0

#bigdecimal
BigDecimal(1.0e + 25) - BigDecimal(9999999999999999900000000.0)
#=> 100000000



<1>精度相差1亿!相当严重吧?

除了精确度误差只有原来的数字的0.000000000000001%左右。真的是由你来决定这是否是一个问题。但是通过使用 BigDecimal 可以解决这个问题,因为它具有任意的精度。你唯一的限制是Ruby可用的内存。



第二个问题是浮点不能精确地表示所有分数。尤其是,它们在 分数方面有问题,因为Ruby(和大多数其他语言)中的浮点数是浮点数。例如,小数 0.2 是一个永久重复的二进制小数( 0.001100110011 ... )。这不能准确地存储在二进制浮点,不管精度如何。



当你舍入数字时,这可能会有很大的不同。考虑:

 #float 
(0.29 * 50).round
#=> 14#不正确

#bigdecimal
(BigDecimal(0.29)* 50).round
#=> 15#正确的

BigDecimal 可以描述<精确到十进制分数。但是,也有小数部分不能用小数部分精确描述。例如 1/9 是一个重复的十进制小数( 0.1111111111111 ... )。

同样,当你围绕一个数字时,这会咬你。考虑:

 #bigdecimal 
(BigDecimal(1)/ 9 * 9/2).round
#=> 0#不正确

在这种情况下,使用十进制浮点数仍然会 给出一个舍入错误。

一些结论:


  • 十进制浮点数真棒如果你需要任意的精确浮点数,那么Ruby的 BigDecimal 也可以很好地工作,如果你使用(科学)数据,你通常会处理固定的精度数字, Ruby的内置的浮点数可能就足够了。

  • 在所有情况下,永远都不能期望使用任何种浮点数的算术。 b $ b

Is my understanding correct that with Ruby BigDecimal types (even with varying precision and scale lengths) should calculate accurately or should I anticipate floating point shenanigans?

All my values within a Rails application are BigDecimal type and I'm seeing some errors (they do have different decimal lengths), hoping it's just my methods and not my object types.

解决方案

There are two common pitfalls when working with floating point arithmetic.

The first problem is that Ruby floating points have fixed precision. In practice this will either be 1) no problem for you or 2) disastrous, or 3) something in between. Consider the following:

# float
1.0e+25 - 9999999999999999900000000.0
#=> 0.0

# bigdecimal
BigDecimal("1.0e+25") - BigDecimal("9999999999999999900000000.0")
#=> 100000000

A precision difference of 100 million! Pretty serious, right?

Except the precision error is only about 0.000000000000001% of the original number. It really is up to you to decide if this is a problem or not. But the problem is removed by using BigDecimal because it has arbitrary precision. Your only limit is memory available to Ruby.

The second problem is that floating points cannot express all fractions accurately. In particular, they have problems with decimal fractions, because floats in Ruby (and most other languages) are binary floating points. For example, the decimal fraction 0.2 is an eternally-repeating binary fraction (0.001100110011...). This can never be stored accurately in a binary floating point, no matter what the precision is.

This can make a big difference when you're rounding numbers. Consider:

# float
(0.29 * 50).round
#=> 14  # not correct

# bigdecimal
(BigDecimal("0.29") * 50).round
#=> 15  # correct

A BigDecimal can describe decimal fractions precisely. However, there are fractions that cannot be described precisely with a decimal fraction either. For example 1/9 is an eternally-repeating decimal fraction (0.1111111111111...).

Again, this will bite you when you round a number. Consider:

# bigdecimal
(BigDecimal("1") / 9 * 9 / 2).round
#=> 0  # not correct

In this case, using decimal floating points will still give a rounding error.

Some conclusions:

  • Decimal floats are awesome if you do calculations with decimal fractions (money, for example).
  • Ruby's BigDecimal also works well if you need arbitrary precision floating points, and don't really care if they are decimal or binary floating points.
  • If you work with (scientific) data, you're typically dealing with fixed precision numbers; Ruby's built-in floats will probably suffice.
  • You can never expect arithmetic with any kind of floating point to be precise in all situations.

这篇关于Ruby BigDecimal完整性检查(浮点newb)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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