Ruby数值类型:是和不是

在大多数编程语言中,我们拥有各种数字类型。 Ruby为我们提供了5种不同的选择:

  • 固定编号
  • 比格纳姆
  • 合理的
  • 浮动
  • 大十进制

为什么?

作为世界上的所有事物,我们通常根据其特征将数字分为不同的集合。 最常见的-可能是您在开发软件时接触的所有对象-以下是: 自然数整数有理数无理数

整数

自然数是指以1开头并保持加1的自然数。因此,1、2、3、4、5,…。 整数是相同的数字,但也包括相反方向的相同求和,即0,-1,-2,-3,-4,…。 Ruby对此集合有一个表示形式:抽象类Integer。 它的具体兄弟是Fixnum和Bignum。

  > 10.class 
=> Fixnum

当您将Integer文字(看起来像Integer的东西)提供给解释器时,该语言将尝试将其适合Fixnum实例。

但是有时候这不可能发生。 我们有内存限制,因为Ruby不允许Fixnum大于本地机器字[1](这在处理器之间会有所不同)。

  >整数= 2 **(1.size * 8-2)-1 
=> 4611686018427387903
> integer.class
=> Fixnum
>整数=(整数+ 1).class
=> Bignum
>(-整数-2).class
=> Bignum

处理大于四百亿个基数的数字不是日常工作,因此使用Fixnum应该没问题。 如果将变量的值更改为超出支持范围(大于或小于)的数字,Ruby将负责释放用于保存该值的内存并将其存储在其他位置。 最后一个将被视为无限分配,因此您可以将其用于基本上可以得出的任何整数。

[1]:Ruby核心团队已经在努力定义一种更简单的方法来获取适合Fixnum对象的最大和最小数量。 看一看: https : //bugs.ruby-lang.org/issues/7517

做和不做

几乎不会指望数字超出Fixnum的范围,或者您的语言或数据库提供的Integer的任何较小类型。 如果您使用的是Ruby,只需让它负责实例化并以最有效的方式使用它。 在Fixnum和Bignum之间执行操作会正常工作,并且(之后)将测试使用更简单的类进行计算结果的可能性。 换句话说,例如,将两个Bignum相减可能会导致Fixnum。

另外,在Ruby中使用Integer文字时,您可以在数字之间任意添加下划线。 129_990比129990更容易理解。

合理性

不幸与否,我们不能仅用整数表示所有内容。 取整数1并除以2。您可以用两种不同的方式查看结果:简单地1/2(作为有效操作,但不适合整数集)或0.5。 由于可以用比率或整数之间的除法完美表示,所以它们都称为“ 有理数 ”。

Ruby确实通过Rational类提供了一种表示和执行Rational数字之间的计算的方法。 与Fixnum / Bignum相比,Ruby将始终假设您在使用Integers和Rationals调用数学函数后期望使用Rational实例。

  >理性(1、2) 
=>(1/2)
>理性= Rational(1,2)+ Rational(1,2)
=>(1/1)
>合理类
=>理性
> Rational.to_i
=> 1

有理数也可以假定与无理数相同的“ Ruby形式”。

做和不做

当对获得精确结果极为重要,但您不知道要处理的数字形式时,使用Rational类特别有用。 如果可能出现不适合Integer的内容,请使用Rational类。

  > 0.5r 
=>(1/2)

如果您要使用硬编码的推理,Ruby也可以帮助您使用文字。 用十进制形式写数字(带点的数字)并附加r。

当在可读性和精度方面的问题不太重要时,请不要使用它。 如果不确定它是否正确,请假设它为真,然后再进行修复。 您的代码可以修改,但精度可能会导致数据永久丢失。

非理性

并非所有数字都可以用比率表示。 但是,同样的道理给我们带来了好消息:没关系。 在计算pi的值时,我们通常不需要超过两个小数位(即使实际pi具有无限大)。

  >数学:: PI 
=> 3.141592653589793
>数学:: PI.class
浮动

为了在Ruby中表示无理数(也包括理性),我们有自己的算术浮点数。

  >(2.0-1.1)== 0.9 
=>错误
>(2.0-1.1)
=> 0.8999999999999999
>(2.0-1.1 + 1.1)== 2.0
=>是的

在这里,即使是经验丰富的软件开发人员也学会了不要问自己那么多。 如果您有耐心,则可以更好地理解IEEE 754中的这组数字。

为什么??!!! 如此简单……在哪个世界中,2.0-1.1与0.9会有所不同,并且几乎在每台已知的计算机中都实现了这一功能?

原因是:没关系! 浮点数,俗称“ Float”(其Ruby表示法的同义词)对任何可能真的很大甚至无限的数都施加了限制,但是您并不十分在乎精度。 供您使用,无论结果是0.8999999999999999还是0.9。 两者都会让您感到高兴,并感谢这个您称为“计算机”的宝贵而又庞大的计算器。

  >浮点= 0.00000000000000000000000000000000000000000000000000000000000000000000000009 
=> 0.0
> float.class
浮动
> float.zero?
=>是的

使用带点的任何数字作为小数点分隔符,Ruby会以Float实例取悦您。

发生这种情况是因为1985年IEEE定义的标准(以及Ruby在其内部使用的标准)以有限的精度存储数字。 即使您输入10亿小数位,它也不会超过15。

  > 2.0-0.0000000000000006 
=> 1.9999999999999993

在2.0–0.0000000000000006之类的命令中,您将一个文字用于浮点数2.0,将另一个用于1.1。 第一个将被存储为2 17(2乘以10的平方),另一个将被存储……嗯,问Wolfram | Alpha。 将数字转换为以2为底的数字(从我们以前使用的以10为底的数字)变成了一个很大的问题。 计算是使用两个Float进行的,所以您希望Float返回,对吗? Ruby将数字四舍五入为您可以接受的数字。 由于最后的4不能精确到小数点后15位,因此决定将4变成3。

如果您需要更高的精度,但仍然不需要像Rational那样精确,那么Ruby会为您提供BigDecimal类。 最后一个(内部)将数字转换为以10000为底的数字(更难于“用尽空间”)和任意精度。

  > BigDecimal('500e1000') 
=>#
> BigDecimal('500e1000')。to_f
=>无限

BigDecimal是唯一没有文字形式的数字类。 它期望您提供一个字符串来初始化一个。 在前面的示例中,我将由500表示的数字提高到1000的幂。 尝试将其转换为Float,我得到了一个全新的东西:Infinity。 Ruby试图使其适应基本精度,但发现超出了范围。 “好的,这似乎是一个有效的数字,但我无法处理。 这超出了我的想象。 甚至不是真正的无限,对于那些粗心的Float来说,它就是如此。

  > 1 / 0.0 
=>无限
> -1 / 0.0
=>-无穷大

超出范围的计算也会发生同样的情况。 “我尽了最大的努力,但这仍然更大。”

  > 0 / 0.0 
=> NaN
> Float :: INFINITY / Float :: INFINITY
=> NaN
> 0 *浮点数:: INFINITY
=> NaN

为了进行计算,Float甚至不知道如何开始,它从“不是数字”引入了特殊值NaN。

做和不做

如果2.0和1.9999999999999999对您的用户无害,请手握Float,然后在黄砖路上快乐地行走。 否则,如果需要控制舍入,请考虑其他替代方法。 浮点数本身定义了舍入机制,但是将其放在小数点后第n位并不是一件令人愉快的工作。 对于通常总是(仅)保留小数点后两位的货币,您的业务逻辑可以将Integer 1000解释为$ 10.00。