如何解决Ruby 中的浮点值是立即数吗?
根据Ruby FAQ,fixnum
(持有integer
),boolean
和nil
是立即数。在我的理解中,一个拥有立即数的对象总是完全一样的,不管它被分配了多少个变量。这在 Ruby 中很容易验证:
# Immediate values
i1 = 1
i2 = 1
i1.object_id # the same as i2's id
i2.object_id # the same as i1's id
# Not immediate values
s1 = "1"
s2 = "1"
s1.object_id # NOT the same as s2's id
s2.object_id # NOT the same as s1's id
float
似乎也是直接值:
f1 = 2.1
f2 = 2.1
f1.object_id # the same as f2's id
f2.object_id # the same as f1's id
但是,我找不到任何关于 float
是直接值的引用。 Ruby 中的浮点值是立即数吗? float
在 Ruby 中如何工作?
解决方法
立即数的概念在 Ruby 中不存在
现在,您可能会问“当 Ruby 没有立即值时,为什么 Ruby 常见问题解答会谈论立即值?”这个问题的答案是,不幸的是,Ruby 社区中的很多文档混淆了编程语言的概念,称为“Ruby”和众多 Ruby 语言之一。实现,不幸的是通常也被称为“Ruby”,尽管它的“官方”名称是“YARV”。
例如,如果您检查 ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification,您将发现没有提及直接值,无论是在该名称下,还是在任何其他名称下的类似概念。它们根本不存在。
还要注意有关您链接到的直接值的部分中的以下警告:
此部分或部分内容可能已过时或需要确认。
所以,我们有两个问题:
- 本节仅讨论一个 Ruby 的具体实现,但并没有说清楚,并且
- 即使是特定于实现的信息也已过时。
在当前版本的 YARV 中,Fixnum
不再存在。过去,Integer
是一个抽象类,有两个具体的子类:Fixnum
和 Bignum
。 Fixnum
是直接值,Bignum
不是。
但是,根据您运行的是 32 位还是 64 位,完全相同文字可以是 Fixnum
,因此是一个立即数,或者是 {{1} }.在 32 位上 Bignum
可以存储 31 位,在 64 位上 Fixnum
可以存储 63 位。请注意,这仅适用于 YARV,例如JRuby 将在 32 位和 64 位平台上存储 64 位(不仅仅是 63 位)。
如今,Fixnum
和 Fixnum
不再存在,而 Bignum
是具体类,而不是抽象类。但是,优化实际上仍在执行:YARV 仍然在 32 位系统上将 31 位 Integer
优化为 fixnum 并且64 位系统上的 63 位 Integer
,JRuby 仍然优化 64 位 Integer
。
JRuby 对 Integer
进行了很长时间的优化。在 JRuby 中,Float
是直接值。然而,在 YARV 中,没有这样的优化。 Float
不是直接值。然而,最近 YARV 引入了 flonum 的概念。 Flonum 是适合 62 位的 Float
。 Flonum 是直接值。此外,此优化仅在 64 位平台上执行,flonums 在 32 位平台上完全禁用。
另请注意,FAQ 完全忽略了 Symbol
,这些 Object#object_id
在许多实现中都是直接值,包括 YARV。
所以,回答您的问题:
Ruby 中的浮点值是立即数吗?
没有。可能是。其中一些,有时。
不,Float
在 Ruby 中不是直接值,如果“Ruby”指的是“Ruby 编程语言”。 Ruby 语言没有立即值的概念。
也许,Float
是 Ruby 中的直接值,也许不是,如果“Ruby”的意思是“所有现有 Ruby 实现的集合”。在某些实现中,在某些情况下,某些 Float
值可能是也可能不是立即值。
某些 Float
有时是直接值,如果“Ruby”是指“YARV”。在 64 位平台上,Float
的子集是直接值,但不是全部。在 32 位平台上,没有一个是立即值。
无关
你只能通过改变一个值来判断它是否是立即数。但是,执行此类优化的所有值都是不可变的,不能有实例变量,也不能有单例方法或单例类。
因此,实际上无法判断某物是否为直接值。这是一个私有的内部优化细节。
这意味着您可以完全忽略这个概念,因为它无法使您的程序以不同的方式运行。
Float
怎么样?
首先:BasicObject#equal?
是可憎的。它不应该存在。忘记你曾经听说过它。
它违反了面向对象的基本原则之一,即模拟另一个对象的对象应该与该对象无法区分。 (同样适用于 frozen String
literals。)
但其次,这里有一个反例:
object_id
糟糕。我破坏了您的测试:{{3}} 不是直接对象,但会自动删除重复数据。
,在 YARV 中,具有直接值的对象与它们的 object_id
密切相关,类似于它们内部的 VALUE
。 (见Creating Extension Libraries for Ruby)
具有“立即值”的对象在其 VALUE
中完全编码。因此,此类对象在每个 Ruby 进程中都具有相同的 object_id
。 (使用相同的 Ruby 二进制文件)
在 Ruby 中,调用 ObjectSpace._id2ref
是一种检索这些对象的方法:(在某些时候,如果您继续循环,这还将枚举来自核心和 stdlib 的非直接对象)
0.upto(31) do |i|
printf('%08b ',i)
begin
obj = ObjectSpace._id2ref(i)
printf('%-10s %s',obj.class,obj.inspect)
rescue RangeError
print('-')
end
puts
end
输出:(在 64 位机器上)
00000000 FalseClass false
00000001 Integer 0
00000010 Float 2.0
00000011 Integer 1
00000100 -
00000101 Integer 2
00000110 Float -2.0
00000111 Integer 3
00001000 NilClass nil
00001001 Integer 4
00001010 Float 2.0000000000000004
00001011 Integer 5
00001100 -
00001101 Integer 6
00001110 Float -2.0000000000000004
00001111 Integer 7
00010000 -
00010001 Integer 8
00010010 Float 2.000000000000001
00010011 Integer 9
00010100 TrueClass true
00010101 Integer 10
00010110 Float -2.000000000000001
00010111 Integer 11
00011000 -
00011001 Integer 12
00011010 Float 2.0000000000000013
00011011 Integer 13
00011100 -
00011101 Integer 14
00011110 Float -2.0000000000000013
00011111 Integer 15
您可以看到 3 种类型的对象:
-
false
、true
和nil
00000000 FalseClass false 00001000 NilClass nil 00010100 TrueClass true
-
整数:
00000001 Integer 0 00000011 Integer 1 00000101 Integer 2 00000111 Integer 3
-
浮动:
00000010 Float 2.0 00000110 Float -2.0 00001010 Float 2.0000000000000004 00001110 Float -2.0000000000000004
在 64 位机器上,object_id
是一个可以容纳 64 位的 unsigned long
。
整数的 object_id
以二进制 1
结尾,所以还有 63 位表示覆盖范围的整数值:
-4611686018427387904..4611686018427387903
超出该范围的整数将成为非直接对象。
浮点数的 object_id
以二进制 10
结尾,因此还有 62 位用于对浮点值进行编码,它涵盖了所有(双精度)浮点数的 1/4。这些在内部被称为“flonum”。
64 位机器上 Ruby 2.0 中的 YARV introduced flonums。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。