Differencebetween Integer and Fixnum

What is the difference between Integer and Fixnum?

You never "use" Integer. It is an abstract class whose job is to endow its children (Fixnum and Bignum) with methods. Under effectively no circumstances will you ever ask for an object's class and be told that it is an Integer.

Ruby Numeric vs Integer

Numeric.descendants
#=> [BigDecimal, Date::Infinity, Integer, Fixnum, Float, Bignum, Rational, Complex]

So if you need to define a method for both Fixnum and Float you can do it in Numeric (not talking about the open classes monkeypatching issues)

How can a Fixnum (Integer) and a Float compare as equal in Ruby?

Are you sure that expressions like 0.9 == 0.9 is not guaranteed? I don't think so. It succeeds on my machine, and even though there is rounding error, the algorithm should always map the same literal expression to the same float with the same rounding error. For example, if the expression "0.9" were to be expressed internally as 0.900001, it will always be so. It doesn't get mapped sometimes to 0.900000 and sometimes to 0.900002. So equality should be guaranteed.

Regarding comparison between Fixnum and Float, if a fixnum literal is converted to a float, it would also be mapped to the same float as if it were a float from the beginning, with the same rounding error. In other words, the following two processes end up with the same float:

  • Literal "1.0" → Some internal float with rounding error (say, 0.999999)
  • Literal "1" → Internal fixnum 1 → Some internal float with rounding error (0.999999)

Edit Or, as fmendez says, if integers are mapped internally precisely to a float, then floats that exactly correspond to an integer (like "1.0", "2.0", etc.) do not have any rounding error within the internal float expression. So equality would be guaranteed anyway.

12345.class returning 'Integer' not 'Fixnum' in Ruby

It depends on the Ruby version. From Ruby 2.4.0 we have just Integers, no more Fixnums and Bignums distinction

https://www.ruby-lang.org/en/news/2016/12/25/ruby-2-4-0-released/

Some `Fixnum` properties

In Ruby, most objects require memory to store their class and instance variables. Once this memory is allocated, Ruby represents each object by this memory location. When the object is assigned to a variable or passed to a function, it is the location of this memory that is passed, not the data at this memory. Singleton methods make use of this. When you define a singleton method, Ruby silently replaces the objects class with a new singleton class. Because each object stores its class, Ruby can easily replace an object's class with a new class that implements the singleton methods (and inherits from the original class).

This is no longer true for objects that are immediate values: true, false, nil, all symbols, and integers that are small enough to fit within a Fixnum. Ruby does not allocate memory for instances of these objects, it does not internally represent the objects as a location in memory. Instead, it infers the instance of the object based on its internal representation. What this means is twofold:

  1. The class of each object is no longer stored in memory at a particular location, and is instead implicitly determined by the type of immediate object. This is why Fixnums cannot have singleton methods.

  2. Immediate objects with the same state (e.g., two Fixnums of integer 2378) are actually the same instance. This is because the instance is determined by this state.

To get a better sense of this, consider the following operations on a Fixnum:

>> x = 3 + 7
=> 10
>> x.object_id == 10.object_id
=> true
>> x.object_id == (15-5).object_id
=> true

Now, consider them using strings:

>> x = "a" + "b"
=> "ab"
>> x.object_id == "ab".object_id
=> false
>> x.object_id == "Xab"[1...3].object_id
=> false
>> x == "ab"
=> true
>> x == "Xab"[1...3]
=> true

The reason the object ids of the Fixnums are equal is that they're immediate objects with the same internal representation. The strings, on the other hand, exist in allocated memory. The object id of each string is the location of its object state in memory.

Some low-level information

To understand this, you have to understand how Ruby (at least 1.8 and 1.9) treat Fixnums internally. In Ruby, all objects are represented in C code by variables of type VALUE. Ruby imposes the following requirements for VALUE:

  1. The type VALUE is is the smallest integer of sufficient size to hold a pointer. This means, in C, that sizeof(VALUE) == sizeof(void*).

  2. Any non-immediate object must be aligned on a 4-byte boundary. This means that any object allocated by Ruby will have address 4*i for some integer i. This also means that all pointers have zero values in their two least significant bits.

The first requirement allows Ruby to store both pointers to objects and immediate values in a variable of type VALUE. The second requirement allows Ruby to detect Fixnum and Symbol objects based on the two least significant bits.

To make this more concrete, consider the internal binary representation of a Ruby object z, which we'll call Rz in a 32-bit architecture:

MSB                                   LSB
3 2 1
1098 7654 3210 9876 5432 1098 7654 32 10
XXXX XXXX XXXX XXXX XXXX XXXX XXXX AB CD

Ruby then interprets Rz, the representation of z, as follows:

  1. If D==1, then z is a Fixnum. The integer value of this Fixnum is stored in the upper 31 bits of the representation, and is recovered by performing an arithmetic right shift to recover the signed integer stored in these bits.

  2. Three special representations are tested (all with D==0)

    • if Rz==0, then z is false
    • if Rz==2, then z is true
    • if Rz==4, then z is nil
  3. If ABCD == 1110, then 'z' is a Symbol. The symbol is converted into a unique ID by right-shifting the eight least-significant bits (i.e., z>>8 in C). On 32-bit architectures, this allows 2^24 different IDs (over 10 million). On 64-bit architectures, this allows 2^48 different IDs.

  4. Otherwise, Rz represents an address in memory for an instance of a Ruby object, and the type of z is determined by the class information at that location.

Size of numeric data types in Ruby

I know that Ruby has Float for real, Fixnum and Bignum for int.

This is not true.

Float does not represent real numbers, it represents floating point numbers. In fact, in the general case, representing real numbers in a physical computer is impossible, as it requires unbounded memory.

Fixnum and Bignum are not part of Ruby. Ruby only has Integer. The Ruby Specification allows different implementations to have implementation-specific subclasses, but these are then specific to that particular implementation (e.g. YARV, Opal, TruffleRuby, Artichoke, JRuby, IronRuby, MRuby, etc.), they don't have anything to do with Ruby.

In fact, even knowing the implementation is not enough, you have to know the exact version. For example, YARV had Fixnum and Bignum as subclasses in the past, but now it doesn't anymore, it only has Integer. And even back when it had them, that was still not enough, because they actually had different sizes depending on the platform.

But what about sizes of this types?

a = 1.23 // size of one Float in bytes?

b = 1 // size of one Fixnum in bytes?

c = 2**65 // = size of one Bignum in bytes?

I am trying to find a standard or specification

Here is what the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification has to say on the matter [bold emphasis mine]:

15.2.8 Integer

15.2.8.1 General description

Instances of the class Integer represent integers. The ranges of these integers are unbounded. However the actual values computable depend on resource limitations, and the behavior when the resource limits are exceeded is implementation-defined.

[…]

Subclasses of the class Integer may be defined as built-in classes. In this case:

  • The class Integer shall not have its direct instances. Instead of a direct instance of the class Integer, a direct instance of a subclass of the class Integer shall be created.
  • Instance methods of the class Integer need not be defined in the class Integeritself if the instance methods are defined in all subclasses of the class Integer.
  • For each subclass of the class Integer, the ranges of the values of its instances may be bounded.

The ISO Ruby Language Specification does not mandate any particular size or representation for Integers, nor does it specify any methods for querying this information.

15.2.9 Float

15.2.9.1 General description

Instances of the class Float represent floating-point numbers.
The precision of the value of an instance of the class Float is implementation-defined; however, if the underlying system of a conforming processor supports IEC 60559, the representation of an instance of the class Float shall be the 64-bit double format as specified in IEC 60559, 3.2.2.

The ISO Ruby Language Specification does not mandate any particular size or representation for Floats, unless the underlying system supports ISO/IEC 60559, in which case the representation must be as an ISO/IEC 60559 binary64 double float. The ISO Ruby Language Specification does not specify any methods for querying this information.

The ruby/spec does not say anything about the size or precision of Float. In fact, it is very careful to not say anything. For example, if you look at the spec for Float#prev_float, you can see that they are very careful to specify the behavior of Float#prev_float without ever referring to the actual precision.

The ruby/spec for Integer#size does say something about the sizes of specific machine integers in bytes. However, unfortunately, ruby/spec is kind of a mixture between specifications for the behavior of the Ruby Programming Language and the behavior of the YARV Ruby Implementation. I have the feeling that this spec is more like the latter.

For example, the cutoff point between fixnums and bignums in YARV is 231 on 32 bit platforms and 263 on 64 bit platforms, but on JRuby, it is 264 on both 32 bit and 64 bit platforms (and I think TruffleRuby is the same). So, 3000000000 will be a bignum on 32 bit YARV, but a fixnum on 64 bit YARV and JRuby, and 10000000000000000000 will be a bignum on both 32 bit and 64 bit YARV, but will be a fixnum even on 32 bit JRuby. For Opal, I think the cutoff point is different again, I think it is 253. Other implementations may not even distinguish between fixnums and bignums at all, or they may have three or more different kinds of integers.

Also, it is very important to remember that this spec only specifies the return value of the method Integer#size. Nowhere does it say that this is actually how an Integer must be represented in memory.

By the way, you may be confused why I am talking about fixnums and bignums in YARV, when I said above that Fixnum and Bignum have been removed from YARV. Well, the reason is that the separate classes have been removed, but the optimization is still there. Which is another thing: the ISO Ruby Language Specification says that you are allowed to have implementation-specific subclasses of Integer, but it doesn't say what those classes are for. Neither does the ISO Ruby Language Specification force implementors to have optimized implementations for their subclasses, nor does it force implementors to have subclasses for their implementation-specific optimizations.

Compare that to YARV's flonums which are an optimized representation of 62 bit Floats, but they don't have their own class.

So, in (not so) short: the ISO Ruby Language Specification does not say anything about the sizes of Integers, but it does say that if the underlying system supports ISO/IEC 60559, then the representation must be an ISO/IEC 60559 binary64 double float. It does, however, say nothing about the size or representation for cases where the underlying system does not support ISO/IEC 60559.

The ruby/spec is careful not to specify anything about the sizes of Floats, but it does specify the return value of the Integer#size method. It does, however, not specify that this return value must in any way correspond to how Integers are actually represented.

Here's what the RDoc for Integer#size in YARV has to say [bold emphasis mine]:

sizeint

Returns the number of bytes in the machine representation of int (machine dependent).

So, it only says that it returns the number of bytes the Integer would have in the machine representation, but not that this is necessarily the way that it is actually represented. And it clearly states that the value is machine dependent.

Ruby Fixnum comparison

If puts r['effortRemaining'].inspect comes out in quote marks, that means it is a string and you need to convert it to a number before you compare it.

This should work:

if r['effortRemaining'].to_f > 0


Related Topics



Leave a reply



Submit