- vừa được xem lúc

Floating-point error và cách giải quyết trong Ruby on Rails

0 0 42

Người đăng: yongsokheng

Theo Viblo Asia

Khi làm việc với việc tính toán số, nhất là với tài chính hoặc tiền, độ chính xác là vô cùng quan trọng. Thậm chỉ dù là sai số quá ít cũng gây ra độ lệnh lớn khi số tiền đó càng lớn. Thông thường bạn hay dùng float hoặc double để làm data type. Nhưng bạn có đề ý về việc sai lệnh khi sử dụng data type này không?

0.1 + 0.2 = 0.30000000000000004
1.2 + 1.9 = 3.0999999999999996
1.2 - 1.0 = 0.19999999999999996
3.19 + 8.2 == 11.389999999999999
5.89 + 2.07 == 7.959999999999999
3.26 + 5.12 == 8.379999999999999
...

Vấn đề này được gọi là Floating-point error. Ngôn ngữ lập trình nào cũng đều gặp floating-point error này. Bạn có thể xem các ví dụ dưới này:

https://github.com/ellenaua/floating-point-error-examples/tree/master/examples

Vậy bạn nên nhớ rằng: Đừng sử dụng floating point number với tiền.

Vậy nên giải quyết như thế nào?

Solution

1. Dùng kiểu dữ liệu DECIMAL trong database

Để đảm bảo độ chính xác trong database, trong MySQL, bạn nên sử dụng kiểu dữ liệu DECIMAL. DECIMAL có 2 phần: precisionscale. Để đảm báo độ lệnh ít nhất trong việc tính toán, bạn nên để scale: 4

class CreatePayment < ActiveRecord::Migration[5.2] def change create_table :payments do |t| t.decimal :amount, precision: 19, scale: 4 t.timestamps end end
end

Nếu bạn check trong database sẽ có kiểu dữ liệu DECIMAL(19, 4)

Rails sẽ tạo column :amount với kiểu DECIMAL. Vậy lúc lưu vào, lấy ra, hoặc tính toán trong MySQL nó cũng dùng kiểu DECIMAL.

Payment.create! amount: 10.5
amount = Payment.first.amount # Result: 0.105e2
amount.to_f # Result: 10.5

2. Dùng BigDecimal() lúc tính toán

Phần trên, data đã là kiểu DECIMAL trong database rồi, lúc tính toán trong database thì không phải là vấn đề nữa. Nhưng sau khi lấy ra và phải tính toán hoặc tính toán số ở ngoài, bạn phải convert số đó thành DECIMAL đã rồi mới tính toán. Trong Ruby có hàm BigDecimal()bạn có thể dùng được.

Bạn xem ví dụ sau đây:

1.2 + 1.9
=> 3.0999999999999996 total = BigDecimal("1.2") + BigDecimal("1.9")
=> 0.31e1 total.to_f => 3.1 1.2 - 1.0 => 0.19999999999999996 amount = BigDecimal("1.2") - BigDecimal("1.0")
=> 0.2e0 amount.to_f
=> 0.2 ... 

Ngôn ngữ lập trình nào cũng có hàm hoặc lib để bạn có thể dùng để tính toán decimal. Như trong javascript, bạn có thể dùng: numeral.js hoặc big.js hoặc bigdecimal.js , ...

Ví dụ numeral.js

Bạn có thể dùng các hàm của nó để tình toán:

.add()
.subtract()
.multiply()
.divide()

Ví dụ

1.2 + 1.9
=> 3.0999999999999996 numeral(1.2).add(1.9).value()
=> 3.1 1.2 - 1.0
=> 0.19999999999999996 numeral(1.2).subtract(1.0).value()
=> 0.2

lib khác cũng tương tự, bạn có thể vào xem chi tiết trong github của nó.

Kết luận:

Vấn đề về Floating-point number này là rất nguy hiểm và hay gặp với các ngôn ngữ lập trình. Nếu bạn không đề ý sẽ gây ra lỗi sai lệnh lớn. Bài này sẽ giúp bạn chú ý hơn trong khi làm dự án tính toán số, nhất là với tài chính, tiền, ...

References:

https://spin.atomicobject.com/2014/08/14/currency-rounding-errors/

https://millarian.com/rails/precision-and-scale-for-ruby-on-rails-migrations/

http://numeraljs.com/

https://github.com/MikeMcl/big.js/

https://github.com/MikeMcl/bignumber.js/

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tôi cá là bạn không biết những điều này - Ruby on rails ( Phần 2)

Các bạn có thể theo dõi phần 1 ở đây :. https://viblo.asia/p/toi-ca-la-ban-khong-biet-nhung-dieu-nay-ruby-on-rails-phan-1-WAyK8DDeKxX. 5.

0 0 222

- vừa được xem lúc

Rails Memoization

Memoization is a process that can be used to speed up rails methods. It caches the results of methods that do time-consuming work, work that only needs to be done once. Here is an example. Example.

0 0 48

- vừa được xem lúc

Tại sao Rails lại dùng cả Webpack lẫn Sprocket?

Khi Rails 6 được ra mắt, có thể bạn đã từng tự hỏi. WTF, sao Webpack đã được add vào rồi, mà Sprocket vẫn tồn tại thế kia . Chẳng phải Webpack và Sprocket được dùng để giải quyết chung một công việc hay sao. Hoặc cả đây:.

0 0 59

- vừa được xem lúc

Bạn nên sử dụng Elasticsearch trong ứng dụng Ruby On Rails như thế nào?

Elasticsearch là một công cụ phân tích và mã nguồn mở mạnh mẽ và linh hoạt, phân tán, theo thời gian thực. Đó là tiêu chuẩn vàng trong công nghệ tìm kiếm.

0 0 80

- vừa được xem lúc

Form object pattern trong rails

1.Mở đầu.

0 0 111

- vừa được xem lúc

Sử dụng Twilio để gửi SMS trong ứng dụng Ruby on Rails

Ngoài cách xác nhận tài khoản hay gửi thông báo bằng email thì hôm nay mình sẽ hướng dẫn các bạn 1 cách nữa là thông qua SMS/Voice. Công cụ sử dụng sẽ là gem Twilio. Installation. Để cài đặt bằng Bundler, hãy lấy phiên bản mới nhất:.

0 0 67