Ruby中是否有浮点数立即值?

3
根据Ruby FAQfixnum(代表整数)、booleannil 都是即时值。我理解中,一个持有即时值的对象无论被分配给多少个变量,始终完全相同。这在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是立即值吗?float在Ruby中是如何工作的?

1
这里有相关内容(https://www.ruby-lang.org/en/documentation/faq/6/),在“语法”下的第一项。浮点数不是立即值。请参阅此处的页面264(http://www.cs.uni.edu/~wallingf/teaching/agile-may2010/ruby/programming-ruby.pdf)以获取有关实现的信息。 - Cary Swoveland
1
如果您发现任何答案有帮助,请选择您喜欢的答案。 - Cary Swoveland
2个回答

3

在Ruby中不存在“立即值”的概念

现在,你可能会问:“为什么Ruby FAQ谈到了立即值,而Ruby并没有立即值?”这个问题的答案是,不幸的是,在Ruby社区的很多文档中,混淆了被称为“Ruby”的编程语言的概念和许多Ruby实现之一的概念,这个实现也经常被称为“Ruby”,尽管它的“官方”名称是“YARV”。

例如,如果你查看ISO/IEC 30170:2012 信息技术-编程语言-Ruby规范,你将找不到任何有关立即值的提及,无论是以这个名字还是以其他任何名字类似的概念。它们根本不存在。

请注意,在你链接到的关于立即值的部分中,还有以下警告:

这个部分或其中的某些部分可能已经过时或需要确认。

所以,我们有两个问题:

  1. 这一部分仅涉及 Ruby 的一个具体实现,而不是所有实现,但它并没有明确说明这一点;
  2. 即便是与具体实现有关的信息也已过时。

在当前版本的 YARV 中,Fixnum 已经不存在了。过去,Integer 是一个抽象类,有两个具体子类:FixnumBignumFixnum 是立即数,而 Bignum 则不是。

然而,根据你是在32位还是64位运行,完全相同的文字可能是Fixnum(即立即值),也可能是Bignum。在32位系统上,Fixnum可以存储31位,在64位系统上,Fixnum可以存储63位。请注意,这仅适用于YARV,例如JRuby将在32位和64位平台上都存储64位(不仅仅是63位)

现在,FixnumBignum已不存在,Integer是一个具体类,而不是抽象类。但是,优化实际上仍在进行:YARV在32位系统上仍将31位的Integer 优化fixnum,在64位系统上将63位的Integer 优化,而JRuby则 仍然 优化64位的Integer

JRuby长期以来一直优化了Float。在JRuby中,Float是即时值。然而,在YARV中,没有这样的优化。 Float不是即时值。然而,最近,YARV引入了flonum的概念。Flonum是适合于62位的FloatFlonum是即时值。此外,此优化仅在64位平台上执行,在32位平台上完全禁用flonum
还要注意的是,FAQ完全忽略了Symbol,它们是许多实现中的即时值,包括YARV。
因此,回答您的问题:

Ruby中的float是即时值吗?

不是。也许。有些时候是。
如果您所说的“Ruby”是指“Ruby编程语言”,则Float在Ruby中不是即时值。 Ruby Language没有即时值的概念。
也许在Ruby中,Float是立即值,也可能不是,如果你指的是“所有现有的Ruby实现集合”,则情况各不相同。在一些实现中,在某些情况下,一些Float值可能是或可能不是立即值。
如果你指的是“YARV”,一些Float值有时是立即值。在64位平台上,Float的一个子集是立即值,但并非全部。在32位平台上,没有任何Float是立即值。
这是无关紧要的。
你只能通过变异来确定一个值是否为立即值。但是,所有进行此类优化的值都是不可变的,不能有实例变量,也不能有单例方法或单例类。
因此,实际上不可能确定某个东西是否为立即值。这是一个私有的内部优化细节。
这意味着你可以完全忽略这个概念,因为它无法使你的程序运行不同。
那么object_id呢?
首先:Object#object_id 是一个可恶的东西,它不应该存在。最好忘记你曾经听说过它。
它违反了面向对象编程的基本原则之一,即模拟另一个对象的对象应该与该对象无法区分。(同样适用于BasicObject#equal?。)
但第二个例子是这样的:
# frozen_string_literal: true

# Not immediate values
s1 = '1'
s2 = '1'

s1.object_id
#=> 60
# SAME as `s2`'s id

s2.object_id
#=> 60
# SAME as `s1`'s id

糟糕,我破坏了你的测试:冻结的String字面值不是直接对象,但它们会自动去重。


2
在YARV中,具有立即值的对象与其类似于内部VALUEobject_id密切相关。(参见为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

你可以看到三种类型的物体:
  1. false, true and nil

    00000000  FalseClass false
    00001000  NilClass   nil
    00010100  TrueClass  true
    
  2. Integers:

    00000001  Integer    0
    00000011  Integer    1
    00000101  Integer    2
    00000111  Integer    3
    
  3. Floats:

    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”。

YARV在64位机器上的Ruby 2.0中引入了flonum


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接