Tất cả chúng ta đã học về số dấu phẩy động ở trường. Trái ngược với số nguyên không có dấu thập phân.
Người ta không cần phải là một chuyên gia toán học để biết rằng số năm là một số nguyên trong khi 5,43 là một số dấu phẩy động. Rõ ràng, tất cả chúng ta đều quen thuộc với dấu thập phân, đây là điểm phân biệt rõ ràng giữa hai dấu thập phân.
Tất nhiên, không có gì ngạc nhiên khi float được sử dụng trong các ngôn ngữ lập trình và thậm chí còn được phân loại là một kiểu dữ liệu nguyên thủy riêng biệt vì mức độ cần thiết của nó đối với tính toán hàng ngày.
Khi bạn bắt đầu học Python hoặc Java, bạn sẽ sử dụng kiểu dữ liệu float. Đặc biệt, khi bạn tự thực hiện phép chia giữa hai số dấu phẩy động.
Trong Python, ngay cả khi bạn không phải khai báo số dấu phẩy động, việc chia hai trong số những số này có thể dẫn đến một số có cùng kiểu dữ liệu, như minh họa bên dưới:
Chúng ta cũng có thể tạo ra kết quả tương tự trong Java, ngay cả khi chúng ta phải khai báo rõ ràng kiểu dữ liệu nguyên thủy 'float' cho tất cả các biến được sử dụng.
Như người ta có thể nói, kiểu tính toán này là cần thiết cho một số ứng dụng và đó chính là lý do tại sao các phép toán với số dấu phẩy động có sẵn như một tính năng trong cả hai ngôn ngữ lập trình này.
Hoàn toàn khác, nếu bạn đang học Solidity, bạn sẽ nhận thấy rằng nó không bao gồm kiểu dữ liệu số dấu phẩy động.
Thay vào đó, nếu bạn đã viết bất kỳ mã nào trong Solidity, bạn sẽ sử dụng các số thuộc loại số nguyên có dấu hoặc không dấu.
Vì vậy, nếu bạn muốn thực hiện phép tính tương tự - năm chia cho hai - như đã trình bày trong phần trước, thì đây là giao diện của nó, như một phần của hợp đồng thông minh:
Tuy nhiên, kết quả mà bạn thu được sẽ không giống nhau vì Solidity không hỗ trợ kiểu dữ liệu float. Ít nhất, không chỉ được nêu ra.
Cụ thể, Solidity sẽ làm tròn kết quả về 0. Trong trường hợp này, và như được hiển thị ở trên, sẽ dẫn đến giá trị là hai. Hãy coi đây là phép toán modulo trên cả hai số.
Mặc dù trong các trường hợp bình thường, điều này không quan trọng lắm, nhưng đôi khi kết quả có thể dẫn đến lỗi tính toán. Đó là lý do tại sao nên tránh hoặc hoãn hoạt động phân chia càng nhiều càng tốt.
Ngay cả khi quy tắc BODMAS ở trường yêu cầu tất cả học sinh toán thực hiện phép tính chia trước khi nhân, thì điều này không được khuyến khích nếu bạn phải thực hiện phép chia số nguyên trong Solidity.
Hãy tìm hiểu tại sao, với ví dụ đơn giản này, thực hiện phép nhân và phép chia với ba số:
Nếu bạn nhập các số một, ba và năm khi triển khai hợp đồng thông minh, bạn sẽ nhận được cùng một giá trị cho các hàm getResult và getResult2, phải không?
Nếu bạn sử dụng một máy tính đơn giản, bạn sẽ nhận được giá trị float là 1,666, tương ứng với một trong Solidity, nhờ không có giá trị float.
Thật không may, điều này không xảy ra khi bạn kiểm tra kết quả của các hàm getResult và getResult2, như minh họa bên dưới:
Nếu chúng ta thực hiện phép chia trước, chúng ta sẽ nhận được kết quả cuối cùng bằng 0. Trái ngược với giá trị mong đợi của một, khi bạn hoãn thao tác đó đến cuối trong hàm getResult.
Như bạn có thể biết, ngay cả khi chúng tôi đã tính toán giá trị này bằng cách dự đoán việc không có giá trị float, thì vẫn có một lỗi xuất hiện có thể gây ra sự mất độ chính xác. Ngược lại, điều này có thể dẫn đến tổn thất kinh tế có thể bỏ qua dễ dàng.
Vì vậy, làm thế nào để chúng ta ngăn chặn lỗi chia số nguyên? Quan trọng hơn, làm cách nào để tăng độ chính xác cho tính toán của chúng tôi? Hãy cùng tìm hiểu ba cách phổ biến nhất để làm điều này.
Cho rằng có một số cách tiếp cận để khắc phục lỗi này, hãy bắt đầu với cách khắc phục đơn giản nhất và xem xét thêm một số cách trước khi gọi nó là một ngày.
Phương pháp #1: Sử dụng hệ số nhân
Bây giờ, cùng với việc đặt phép chia cuối cùng, một cách để đảm bảo rằng bạn không gặp phải lỗi hoặc giá trị không chính xác là sử dụng hệ số nhân. Trong ví dụ bên dưới, chúng tôi sẽ sử dụng hệ số nhân là 100 cùng với ba số giống như đã sử dụng trước đó.
Bây giờ, khi bạn triển khai hợp đồng với đoạn mã sau và gọi cả hai hàm, đây là kết quả:
Vì đầu ra mong muốn là 1,666 hoặc 166/100, chúng ta có thể thấy rằng giá trị getResult2 cung cấp cho chúng ta độ chính xác cần thiết khi hệ số nhân hoạt động cùng với ba số. Tất nhiên, nếu bạn không sử dụng hệ số nhân như trong hàm getResult, bạn sẽ nhận được 1.
Trong đó 0,666 bị cắt bớt khỏi kết quả, như mong đợi theo mặc định khi bạn sử dụng Solidity. Vì vậy, để lấy giá trị này, tất cả những gì bạn phải làm là chia kết quả cho số nhân.
Như bạn có thể biết, Solidity tiến về 0 khi làm tròn số trong trường hợp cả số nguyên có dấu và không dấu, vì vậy cách khắc phục này cũng hoạt động trong trường hợp số nguyên có dấu, sau khi bạn triển khai và chạy mã bên dưới với các đối số trừ đi một , ba và năm:
Khi nói đến các giá trị được tạo bởi các hàm cho các số nguyên đã ký, chúng ở đây:
Rõ ràng, chúng ta cũng có thể duy trì độ chính xác cho các số nguyên có dấu bằng cách sử dụng hệ số nhân. Mặc dù vậy, có một cách chính xác để làm tròn số nguyên có dấu mà chúng ta sẽ xem xét tiếp theo.
Phương pháp #2: Sử dụng phép chia sàn cho số nguyên có dấu
Bây giờ, nếu nhìn vào dãy số bên dưới, kết quả của phép chia số nguyên giữa hai số nguyên không dấu sẽ được làm tròn gần bằng không. Như trong trường hợp nhận được 1,666, Solidity làm tròn nó thành 1, đây là một số nhỏ hơn.
Tuy nhiên, đối với số nguyên có dấu, kết quả -1,6666 sẽ được làm tròn thành -1, là số lớn hơn trong hai số. Vì vậy, phép chia sàn phải được áp dụng ở đây thay vì phép chia làm tròn thành 0 được triển khai trong Solidity theo mặc định. Vì lợi ích của độ chính xác, tất nhiên.
Nếu kiểu dữ liệu float có sẵn, giá trị -1,666 sẽ được tính. Mặc dù Solidity sẽ làm tròn số này xuống -1, nhưng việc áp dụng phép chia sàn cho các số nguyên đã ký sẽ giảm nó xuống -2.
Khi bạn gọi các hàm getResult và getResult2, chúng ta sẽ nhận được giá trị như hình bên dưới cho các đối số trừ một, ba & năm:
Như bạn có thể biết, getResult tính toán giá trị bằng cách sử dụng phương pháp làm tròn về 0 trong khi getResult2 làm tròn giá trị đó thành số nguyên nhỏ nhất ở đây, nhờ phép chia sàn.
Cách 3: Sử dụng thư viện ABDKMath64x64
Bây giờ, đối với phương thức cuối cùng, chúng ta sẽ sử dụng thư viện ABDKMath64x64, thư viện này sẽ chuyển đổi kết quả của phép chia thành các số điểm cố định.
Tuy nhiên, một lần nữa, sử dụng thư viện này được cho là cải thiện độ chính xác trái ngược với phương pháp làm tròn về 0 có sẵn theo mặc định trong Solidity. Để hiểu đầu ra, chúng ta hãy so sánh kết quả của phép cộng với hàm div, như hình dưới đây:
Khi bạn thêm các đối số 1, 1 và 1 khi triển khai hợp đồng thông minh, bạn sẽ nhận được các giá trị như hình bên dưới:
Không có gì ngạc nhiên khi hàm add trả về một giá trị nguyên là hai, với ba đối số cộng lại thành nhiều như vậy. Đối với hàm div, giá trị int 128 được trả về đại diện cho số điểm cố định 64,64 bit đã ký và bạn có thể thực hiện các thao tác số thông thường với.
Tất nhiên, thư viện ABDKMath64x64 không phải là thư viện duy nhất có thể được sử dụng để cải thiện độ chính xác ngoài việc ngăn lỗi chia số nguyên.
Có một số thư viện khác với một vài ví dụ là Fixidity , DSMath và thư viện BANKEX sử dụng các định dạng số khác nhau. Các định dạng số khác với định dạng số điểm cố định 64,64 bit được sử dụng trong ví dụ trên. Vì vậy, mặc dù các thư viện này có vẻ hữu ích để khám phá, hãy nhớ rằng các định dạng số của chúng sẽ không hoạt động với bất kỳ thư viện nào khác hiện có.