Computer >> Máy Tính >  >> Lập trình >> Ruby

Cách Numbers hoạt động trong Ruby:Hiểu số nguyên, số thực và số thập phân lớn

Ruby 2.4 đã hợp nhất Fixnum &Bignum vào cùng một lớp (Integer ) vì vậy tôi nghĩ đây là thời điểm tốt để xem xét các loại số khác nhau trong Ruby!

Đó là những gì chúng ta sẽ nói trong bài đăng này 🙂

Tổng quan về các loại số

Hãy bắt đầu bằng cách xem xét phân cấp lớp của tất cả các lớp liên quan đến số trong Ruby:

Numeric
  Integer
    Fixnum
    Bignum
  Float
  Complex
  Rational
  BigDecimal (Standard Library)

Như bạn có thể thấy, Numeric lớp là cha của tất cả các lớp số. Hãy nhớ rằng bạn có thể sử dụng ancestors để khám phá các lớp cha cho bất kỳ lớp nào.

Ví dụ :

Fixnum.ancestors - Fixnum.included_modules

[Fixnum, Integer, Numeric, Object, BasicObject]

Bây giờ chúng ta hãy xem các lớp này ở dạng bảng:

Lớp Mô tả Ví dụ
Số nguyên Lớp chính của Fixnum &Bignum 1
Fixnum Các số nguyên phù hợp với kiểu số nguyên hệ điều hành (32 hoặc 64 bit) 1
Bignum Được sử dụng cho các số lớn hơn 111111111111
Phao Số thập phân không chính xác 5.0
Phức tạp Được sử dụng cho công cụ toán học với các số ảo (1 + 0i)
Hợp lý Dùng để biểu diễn phân số (2/3)
BigDecimal Số thập phân chính xác hoàn hảo 3.0

Phao không chính xác

Float lớp trong Ruby được mô tả là "không chính xác" trong tài liệu Ruby chính thức.

Tại sao vậy?

Để tôi cho bạn xem một ví dụ :

0.2 + 0.1 == 0.3
# false

Tại sao điều này là sai?

Hãy xem kết quả của 0.2 + 0.1 :

0.30000000000000004

Một cách chính xác! Đó là ý của chúng tôi khi nói đến sự không chính xác.

Điều này xảy ra do cách lưu trữ float. Nếu bạn cần số thập phân luôn chính xác, bạn có thể sử dụng BigDecimal lớp học.

Float so với BigDecimal

BigDecimal là một lớp cung cấp cho bạn các số thập phân có độ chính xác tùy ý.

Ví dụ :

require 'bigdecimal'

BigDecimal("0.2") + BigDecimal("0.1") == 0.3
# true

Tại sao chúng ta không luôn sử dụng BigDecimal sau đó? Bởi vì nó chậm hơn rất nhiều!

Đây là điểm chuẩn :

Calculating -------------------------------------
          bigdecimal    21.559k i/100ms
               float    79.336k i/100ms
-------------------------------------------------
          bigdecimal    311.721k (± 7.4%) i/s -      1.552M
               float      3.817M (±11.7%) i/s -     18.803M

Comparison:
               float:  3817207.2 i/s
          bigdecimal:   311721.2 i/s - 12.25x slower

BigDecimal chậm hơn 12 lần so với Float và đó là lý do tại sao nó không phải là mặc định 🙂

Fixnum và Bignum

Trong phần này, tôi muốn khám phá sự khác biệt giữa FixnumBignum trước Ruby 2.4.

Hãy bắt đầu với một số mã :

1.class
# Fixnum

100000000000.class
# Bignum

Ruby tạo đúng lớp cho chúng tôi và nó sẽ tự động quảng bá một Fixnum đến một Bignum khi cần thiết.

Lưu ý :Bạn có thể cần một số lớn hơn để nhận được Bignum đối tượng nếu bạn có trình thông dịch Ruby 64-bit.

Tại sao chúng ta cần các lớp khác nhau? Câu trả lời là để làm việc với những con số lớn hơn, bạn cần một cách triển khai khác và làm việc với những con số lớn sẽ chậm hơn, vì vậy chúng ta sẽ gặp phải tình huống tương tự với Float so với BigDecimal .

Các thuộc tính đặc biệt của Fixnums

Fixnum lớp cũng có một số thuộc tính đặc biệt. Ví dụ:id đối tượng được tính bằng công thức.

1.object_id
# 3
20.object_id
# 41

Công thức là:(number * 2) + 1 .

Nhưng còn nhiều điều hơn thế này, khi bạn sử dụng Fixnum không có đối tượng nào được tạo ra cả. Không có dữ liệu nào để lưu trữ trong Fixnum , bởi vì giá trị được lấy từ chính id đối tượng.

Đây chỉ là chi tiết triển khai, nhưng tôi nghĩ sẽ rất thú vị khi biết 🙂

MRI (Matz’s Ruby Interpreter) sử dụng hai macro này để chuyển đổi giữa giá trị và id đối tượng:

INT2FIX(i)  ((VALUE)(((SIGNED_VALUE)(i))<<1 | FIXNUM_FLAG))
FIX2LONG(x) ((long)RSHIFT((SIGNED_VALUE)(x),1))

Điều xảy ra ở đây được gọi là "dịch chuyển bit", di chuyển tất cả các bit sang trái hoặc phải.

Di chuyển một vị trí sang trái tương đương với nhân với 2 và đó là lý do tại sao công thức là (number * 2) + 1 . +1 đến từ FIXNUM_FLAG .

Ngược lại, Bignum hoạt động giống như một lớp bình thường và sử dụng id đối tượng bình thường:

111111111111111.object_id
# 23885808

Tất cả điều này có nghĩa là Fixnum các đối tượng gần với các biểu tượng hơn về cách chúng hoạt động ở cấp thông dịch, trong khi Bignum các đối tượng gần với chuỗi hơn.

Số nguyên trong 2,4

Vì Ruby 2.4 Fixnum &Bignum không được dùng nữa, nhưng đằng sau nó vẫn hoạt động theo cách tương tự.

Ruby tự động chuyển từ loại này sang loại khác.

Không thay đổi lớp học .

Điều này có nghĩa là Integer nhỏ các số vẫn hoạt động giống như một Fixnum .

Tóm tắt

Trong bài đăng này, bạn đã tìm hiểu về các lớp liên quan đến số khác nhau tồn tại trong Ruby.

Bạn đã biết rằng số thực không chính xác và bạn có thể sử dụng BigDecimal nếu độ chính xác quan trọng hơn nhiều so với hiệu suất. Bạn cũng biết rằng Fixnum các đối tượng đặc biệt ở cấp thông dịch, nhưng Bignum s chỉ là các đối tượng thông thường.

Nếu bạn thấy bài đăng này thú vị, đừng quên đăng ký nhận bản tin của tôi ở biểu mẫu bên dưới 🙂