JavaScript trừ hai số float

Tùy thuộc vào những yêu cầu toán học mà ứng dụng của bạn có, bạn có thể phải đặc biệt cẩn thận để đảm bảo rằng các hoạt động của bạn trả về những gì bạn mong đợi đối với độ chính xác (kép) của dấu phẩy động. Đối với thế giới JavaScript nói chung, đây không phải là vấn đề mới

Tìm bổ sung. Bài báo JavaScript

Trên thực tế, đối với thế giới lập trình nói chung, đây không phải là một vấn đề mới. JavaScript thường bị chỉ trích vì “Nó không thể thực hiện toán học một cách chính xác. ” Nếu những tiêu chí tương tự này được áp dụng cho C#, bạn sẽ đạt được kết luận tương tự. Ở phần sau của cột này, tôi sẽ minh họa cách C# và JavaScript, khi có liên quan đến các số dấu phẩy động, hoạt động theo cùng một cách. Trong chuyên mục của ấn bản này, tôi sẽ giới thiệu cho bạn một số đặc điểm toán học của JavaScript và cách khắc phục chúng

Loại số gốc duy nhất của JavaScript là loại 64 bit có độ chính xác kép (dấu chấm động)

Bối cảnh và khái niệm cốt lõi

Nếu bạn là nhà phát triển ở một nơi mà JavaScript không phải là ngôn ngữ chính và bạn được giao nhiệm vụ yêu cầu JavaScript, cho dù trên máy khách hay máy chủ, thì bạn có thể có một số giả định đã được đưa ra dựa trên trải nghiệm chính của mình với C, C# . Giả định đầu tiên của bạn có lẽ là JavaScript có kiểu số nguyên. Giả định thứ hai của bạn có thể là JavaScript xử lý các phép toán giống như ngôn ngữ chính của bạn. Cả hai giả thiết đều sai. Câu hỏi tiếp theo cần giải quyết là TẠI SAO lại như vậy. Để bắt đầu trả lời câu hỏi đó, hãy xem JavaScript hoạt động với một tác vụ đơn giản, thêm hai giá trị và kiểm tra sự bằng nhau. Hình 1 cho thấy một bài toán đơn giản

JavaScript trừ hai số float

Hình 1. Đầu ra JSFiddle minh họa điều đó. 1 +. 2 không bằng. 3

Trong C, C# và các ngôn ngữ có vị trí tương tự khác, kết quả của 0. 1 + 0. 2 == 0. 3 tất nhiên là đúng. Nhưng đó không phải là trường hợp trong JavaScript. Giả định tiếp theo của bạn có thể là JavaScript không thể làm toán. Đây cũng sẽ là một giả định không chính xác. Trước khi bạn có thể hiểu JavaScript hoạt động như thế nào, trước tiên bạn phải giải quyết JavaScript là gì và cụ thể hơn JavaScript là gì trong ngữ cảnh toán học và những loại dữ liệu số mà JavaScript hỗ trợ trực tiếp

Câu trả lời chỉ là một. JavaScript hỗ trợ một loại toán học, số dấu phẩy động 64 bit. Vì tôi đang nói về hệ thống 64-bit (nhị phân), cơ số 2 được áp dụng. Câu hỏi đặt ra là "số dấu phẩy động 64 bit được lưu trữ như thế nào?"


Hình 2. Tiêu chuẩn IEEE 754 để lưu trữ các số dấu phẩy động 64 bit

Xem lại tiêu chuẩn IEEE 754, số nguyên lớn nhất có thể dựa vào để được lưu trữ chính xác một cách an toàn được biểu thị bằng (253) ? . Tại sao chúng ta cần trừ 1?

0
00000000010
0000000000000000000000000000000000000000000000000000

Chỉ bit thứ 10 được lật. Tuy nhiên, bit thứ 10 này, theo sơ đồ lưu trữ được minh họa trong Hình 2, nằm trong phạm vi bit dành riêng cho số mũ. Do đó, nếu bạn trừ đi 1 để được giá trị số nguyên an toàn lớn nhất (9,007,199,254,740,991) thì biểu diễn nhị phân như sau

0
00000000001
1111111111111111111111111111111111111111111111111111

Các bit từ 0 đến 52 được lật. Theo đó, điều này có nghĩa là giá trị 9,007,199,254,740,991. Trong JavaScript, có một cách đơn giản để truy cập số này và số đối âm của nó với các hằng số MAX_SAFE_INTEGER và MIN_SAFE_INTEGER được lưu trữ trong Nguyên mẫu số, như trong Hình 3

JavaScript trừ hai số float

Hình 3. Các hằng số nguyên an toàn tối thiểu và tối đa cung cấp quyền truy cập nhanh vào các số nguyên nhỏ nhất và lớn nhất có thể dựa vào một cách an toàn trong JavaScript

Để hoàn thiện hình minh họa, biểu diễn nhị phân của giá trị số nguyên an toàn nhỏ nhất (-9,007,199,254,740,991) được biểu diễn như sau

1
11111111110
0000000000000000000000000000000000000000000000

Trong trường hợp này, bạn chỉ cần đảo ngược cách lật các bit đối với giá trị số nguyên an toàn lớn nhất

Bây giờ bạn đã biết cách JavaScript hỗ trợ một kiểu số và cách lưu trữ của nó hoạt động từ phối cảnh số nguyên nhỏ nhất và lớn nhất, hãy chuyển sự chú ý của bạn sang bài toán ban đầu với số thập phân. 0. 1 + 0. 2 ===. 3 (kết quả là sai). Có lẽ bạn đã nghe tuyên bố rằng các giá trị dấu phẩy động không thể được đảm bảo là chính xác. Bạn chắc chắn biết đây là trường hợp vì biểu thức 0. 1 + 0. 2 === 0. 3 là sai trong JavaScript. Câu hỏi là. Tại sao nó sai?

Trước tiên, bạn phải nhận ra rằng máy tính chỉ có thể lưu trữ số nguyên. Đó là bản chất của tính toán nhị phân (cơ sở 2). Ví dụ: bạn có các cặp giá trị nhị phân/thập phân sau

  • 0. 0
  • 0. 1
  • 10. 2
  • 11. 3
  • 100. 4
  • 101. 5

Bên ngoài máy tính, bạn thường xử lý các số trong hệ cơ số 10 (mỗi dấu thập phân ở bên phải là giảm đi 10 lần). Để có kết quả thập phân không lặp lại, phân số của bạn phải giảm xuống mẫu số là thừa số nguyên tố của 10. Các thừa số nguyên tố của 10 là 2 và 5. Sử dụng 1/2, 1/3, 1/4 và 1/5 làm ví dụ, khi chuyển đổi sang định dạng thập phân, một số rõ ràng và kết thúc và một số lặp lại. Theo đó, các ví dụ trước, được chuyển đổi thành số thập phân, như sau. . 5,. 33333333?. ,. 25, và. 2. Trong hệ cơ số 10, miễn là số chia của bạn chia hết cho thừa số nguyên tố là 10, bạn sẽ có một kết quả chính xác không lặp lại. Bất cứ điều gì khác lặp lại

Với máy tính, có hệ cơ số 2 (nhị phân) chỉ có 1 thừa số, 2. Đây là bản chất của một hệ thống nhị phân; . Không có lựa chọn thứ ba. Đây là lý do tại sao máy tính chỉ có thể lưu trữ số nguyên. Do đó, để có được mức độ chính xác cao hơn thông qua các số thập phân, phải có một sơ đồ trong phạm vi bit hoạt động để biểu thị các số đó. Trong trường hợp với JavaScript, lược đồ là 64 bit và Hình 2 minh họa cách lược đồ được phân chia giữa các phần bên trái và bên phải của dấu thập phân cũng như dấu (dương hoặc âm)

Tại sao sau đó làm 0. 1 + 0. 2. = 0. 3 trong JavaScript?


hinh 4. Đối với JavaScript, toàn bộ (. 3) không bằng tổng các phần (. 1 +. 2)

Hình 5 minh họa vấn đề tương tự với C#

JavaScript trừ hai số float

Hình 5. Cho dù trong JavaScript hay C#, những cạm bẫy giống nhau có thể tồn tại khi cố gắng kiểm tra sự bằng nhau giữa các số dấu phẩy động

Trên thực tế, các giá trị được minh họa trong Hình 5 là các loại chính xác kép. Nếu trong C#, bạn muốn coi các số là số thực, bạn phải sử dụng hậu tố “f”, như trong Hình 6

JavaScript trừ hai số float

Hình 6. Trong C#, nếu các giá trị được xác định rõ ràng là số float, thì kết quả so sánh sẽ đúng

Tôi đang tham khảo C# vì hai lý do. Đầu tiên, để minh họa khi so sánh tương tự với C#, vấn đề tương tự có thể tồn tại. Thứ hai, vì các ngôn ngữ như C# hỗ trợ nhiều loại số, nên có nhiều cách trong các ngôn ngữ khác ngoài JavaScript để tận dụng các loại khác để giải quyết vấn đề có thể xảy ra khi so sánh các loại động

Quay trở lại câu hỏi cốt lõi trong JavaScript, tại sao (. 1 +. 2). =. 3? . Hãy nhìn vào 0. 1. Là một phân số,. 1 có thể được đại diện tại 1/10. Mọi thứ máy tính làm phải được chia thành hệ nhị phân. Vì vậy, sau đó, những gì là. 1 ở dạng nhị phân? . Số 10 được biểu thị dưới dạng nhị phân là 1010 (bit thứ 2 và thứ 4? Giá trị 2 và 8? được đảo lộn. 2 + 8 == 10). Do đó, phép chia nhị phân như sau. 1010/1. Xem hình 7

JavaScript trừ hai số float

Hình 7. Phép chia dài nhị phân – chia 1 cho biểu diễn nhị phân của 10. 1010

Như bạn có thể thấy, 0011 lặp lại vô tận. Bởi vì chỉ có một không gian hữu hạn để lưu trữ số thập phân lặp lại vô hạn, nên phải thực hiện một số điều chỉnh. Để vừa với giới hạn bit, 64 trong trường hợp này, phải áp dụng làm tròn. Một trang web hữu ích để đánh dấu có thể được tìm thấy ở đây. https. //www. h-schmidt. net/FloatConverter/IEEE754. html. Nếu bạn nhập giá trị cho. 1 và. 2, bạn sẽ thấy biểu diễn nhị phân cho các số đó và giá trị lỗi do chuyển đổi. Trong trường hợp này, bạn sẽ thấy các giá trị lỗi được làm tròn của 1E-9 và 3E-9 cho. 1 và. 2 tương ứng. Theo đó, khi bạn nhìn thấy kết quả của. 1 +. 2, không có gì ngạc nhiên khi thấy lỗi làm tròn được chuyển tiếp; . 1 +. 2, bạn sẽ thấy sự khác biệt trong kết quả cuối cùng được minh họa trong Hình 4

JavaScript thực hiện các phép tính số học với các số dấu phẩy động theo cách nhất quán với các ngôn ngữ lập trình khác

Nhà phát triển phải làm gì? . Vấn đề thực sự là bản thân các số có dấu phẩy động/độ chính xác kép và cách các lỗi có thể xảy ra khi được chuyển đổi sang và từ định dạng nhị phân. Một cách để giải quyết vấn đề là trước tiên loại bỏ khía cạnh dấu phẩy động của mọi thứ và thay vào đó, làm việc với các số nguyên dẫn xuất và sau đó chia kết quả. Hình 8 minh họa một cách để giải quyết vấn đề

JavaScript trừ hai số float

Hình 8. Một phương pháp để tránh các lỗi làm tròn có thể xảy ra do các số dấu phẩy động được chuyển đổi sang và từ định dạng nhị phân

Nếu bạn thực sự muốn tìm hiểu về số học dấu phẩy động, hãy cân nhắc xem lại bài viết này của David Goldberg có tựa đề “Điều mà mọi nhà khoa học máy tính nên biết về số học dấu phẩy động” (https. // tài liệu. tiên tri. com/cd/E19957-01/806-3568/ncg_goldberg. html)

Một cách tiếp cận khác mà bạn có thể cân nhắc là một điểm cuối dịch vụ được hỗ trợ bởi một ngôn ngữ như C, C#, VB, Ruby, Python, v.v. Bất kể bạn thực hiện phương pháp nào, điều quan trọng là phải nhận ra, càng sớm càng tốt, ứng dụng của bạn có thể nhạy cảm với các vấn đề về độ chính xác với số dấu phẩy động. Trong mọi trường hợp, và đặc biệt là trường hợp này, thử nghiệm là bạn của bạn

Chìa khóa rút ra

Nhà phát triển phải làm gì? . Vấn đề thực sự là bản thân các số dấu phẩy động/độ chính xác kép và cách các lỗi có thể xảy ra khi được chuyển đổi sang và từ nhị phân. Bất kể môi trường ngôn ngữ nào, cần phải thận trọng khi xử lý các số dấu phẩy động vì không phải tất cả các số thực đều có thể được biểu diễn chính xác dưới dạng số dấu phẩy động. Một giải pháp đơn giản là không sử dụng số dấu phẩy động. Đối với JavaScript, điều đó thật khó vì số duy nhất mà JavaScript có là dấu phẩy động. Trong trường hợp cần có các hoạt động tiền tệ, cách tiếp cận tốt nhất của bạn rất có thể là để dịch vụ phụ trợ xử lý các phép tính của bạn rồi chuyển dữ liệu trở lại JavaScript dưới dạng chuỗi cho mục đích hiển thị. Tất cả phụ thuộc vào những gì ứng dụng cụ thể của bạn yêu cầu. Nếu tất cả những gì bạn xử lý là các số nguyên tồn tại giữa các số nguyên an toàn tối thiểu và tối đa trong Hình 3, thì bạn không nên gặp phải các vấn đề được mô tả trong bài viết này

Nếu không phải như vậy và thay vào đó bạn xử lý số thập phân, hãy cân nhắc sử dụng thư viện như số thập phân. js. https. //github. com/MikeMcl/thập phân. js/. Điều quan trọng là phải hiểu và nhận ra rằng có tồn tại các vấn đề với số float. Và nếu bạn nghĩ đó không phải là vấn đề sống chết, thì hãy xem xét những gì đã xảy ra vào ngày 25 tháng 2 năm 1991. Ngày đó, có một vụ thất bại tên lửa Patriot. Vụ phóng tên lửa Patriot không đánh chặn được tên lửa Scud, dẫn đến cái chết của 28 U. S. quân nhân. Nguyên nhân? . http. //www-người dùng. môn Toán. ừm. edu/~arnold/thảm họa/người yêu nước. html

Làm cách nào để trừ hai giá trị float trong JavaScript?

Để trừ chính xác hai giá trị float, hãy sử dụng parseFloat() cùng với toFixed() .

Làm cách nào để trừ hai số thập phân trong JavaScript?

Giải pháp. .
Biến số thập phân thành số nguyên. Nói ví dụ, ở đây chúng tôi có hai giá trị. .
Số thập phân lớn nhất của hai số trên là hai(. 09). .
Bây giờ trừ toàn bộ số. .
Bây giờ chia giá trị trừ bằng cách sử dụng giá trị nhân. .
Kết quả cuối cùng và dự kiến ​​sẽ là 23791/100=237. 91
Đó là tất cả

Làm cách nào để trừ hai số trong mã JavaScript?

Bản trình diễn JavaScript. Biểu thức - Toán tử trừ .
bảng điều khiển. log(5 - 3); . 2. ​
bảng điều khiển. đăng nhập (3. 5 - 5); . -1. 5. ​
bảng điều khiển. nhật ký (5 - 'xin chào'); . NaN. ​
bảng điều khiển. nhật ký (5 - đúng); . 4. ​

Tôi có thể trừ số float khỏi int không?

Bạn có thể trừ một số nguyên và số dấu phẩy động bằng toán tử trừ .