Nhân mảng javascript

Số học dấu phẩy động được coi là một chủ đề bí truyền của nhiều người. Điều này khá ngạc nhiên vì dấu chấm động phổ biến trong các hệ thống máy tính. Hầu hết mọi ngôn ngữ đều có kiểu dữ liệu dấu phẩy động; . Bài báo này trình bày một hướng dẫn về các khía cạnh của dấu phẩy động có tác động trực tiếp đến các nhà thiết kế hệ thống máy tính. Nó bắt đầu với thông tin cơ bản về biểu diễn dấu phẩy động và lỗi làm tròn, tiếp tục với phần thảo luận về tiêu chuẩn dấu phẩy động IEEE và kết thúc bằng nhiều ví dụ về cách các nhà chế tạo máy tính có thể hỗ trợ dấu phẩy động tốt hơn.

Danh mục và mô tả chủ đề. (Chính) C. 0 [Tổ chức hệ thống máy tính]. Chung -- thiết kế tập lệnh; . 3. 4 [Ngôn ngữ lập trình]. Bộ xử lý -- trình biên dịch, tối ưu hóa; . 1. 0 [Phân tích số]. Chung -- số học máy tính, phân tích lỗi, thuật toán số (Trung học)

D. 2. 1 [Kỹ thuật phần mềm]. Yêu cầu/Thông số kỹ thuật -- ngôn ngữ; . 3. 4 Ngôn ngữ lập trình]. Định nghĩa và lý thuyết chính thức -- ngữ nghĩa; . 4. 1 Hệ điều hành]. Quản lý quy trình -- đồng bộ hóa

Điều khoản chung. Thuật toán, Thiết kế, Ngôn ngữ

Các từ và cụm từ chính bổ sung. Số không chuẩn hóa, ngoại lệ, dấu phẩy động, tiêu chuẩn dấu phẩy động, tràn dần, số bảo vệ, NaN, tràn, lỗi tương đối, lỗi làm tròn, chế độ làm tròn, ulp, tràn

Giới thiệu

Các nhà xây dựng hệ thống máy tính thường cần thông tin về số học dấu phẩy động. Tuy nhiên, có rất ít nguồn thông tin chi tiết về nó. Một trong số ít cuốn sách về chủ đề này, Tính toán dấu phẩy động của Pat Sterbenz, đã hết bản in từ lâu. Bài viết này là một hướng dẫn về các khía cạnh của số học dấu phẩy động (dấu phẩy động sau đây) có kết nối trực tiếp với việc xây dựng hệ thống. Nó bao gồm ba phần kết nối lỏng lẻo. Phần đầu tiên, , thảo luận về ý nghĩa của việc sử dụng các chiến lược làm tròn khác nhau cho các phép toán cơ bản cộng, trừ, nhân và chia. Nó cũng chứa thông tin cơ bản về hai phương pháp đo lỗi làm tròn, ulps và

     n = n/2
2
     n = n/2
3. Phần thứ hai thảo luận về tiêu chuẩn dấu phẩy động IEEE, đang được các nhà sản xuất phần cứng thương mại chấp nhận nhanh chóng. Bao gồm trong tiêu chuẩn IEEE là phương pháp làm tròn cho các hoạt động cơ bản. Thảo luận về tiêu chuẩn dựa trên tài liệu trong phần. Phần thứ ba thảo luận về các kết nối giữa dấu phẩy động và thiết kế các khía cạnh khác nhau của hệ thống máy tính. Các chủ đề bao gồm thiết kế tập lệnh, tối ưu hóa trình biên dịch và xử lý ngoại lệ

Tôi đã cố gắng tránh đưa ra các phát biểu về dấu phẩy động mà không đưa ra lý do tại sao các phát biểu đó là đúng, đặc biệt là khi các biện minh không liên quan gì phức tạp hơn phép tính cơ bản. Những giải thích không phải là trọng tâm của lập luận chính đã được nhóm lại thành một phần gọi là "Chi tiết" để chúng có thể được bỏ qua nếu muốn. Đặc biệt, phần chứng minh của nhiều định lý xuất hiện trong phần này. Kết thúc mỗi bằng chứng được đánh dấu bằng ký hiệu z. Khi không có bằng chứng, z xuất hiện ngay sau phát biểu của định lý

Lỗi làm tròn

Ép vô số số thực vào một số bit hữu hạn yêu cầu biểu diễn gần đúng. Mặc dù có vô số số nguyên, nhưng trong hầu hết các chương trình, kết quả tính toán số nguyên có thể được lưu trữ trong 32 bit. Ngược lại, với bất kỳ số lượng bit cố định nào, hầu hết các phép tính với số thực sẽ tạo ra các đại lượng không thể được biểu diễn chính xác bằng cách sử dụng nhiều bit đó. Do đó, kết quả của một phép tính dấu phẩy động thường phải được làm tròn để phù hợp trở lại với biểu diễn hữu hạn của nó. Lỗi làm tròn này là tính năng đặc trưng của tính toán dấu phẩy động. Phần này mô tả cách nó được đo lường

Vì hầu hết các phép tính dấu phẩy động đều có lỗi làm tròn, nên có vấn đề gì nếu các phép toán số học cơ bản gây ra lỗi làm tròn nhiều hơn một chút so với mức cần thiết? . Phần thảo luận về các số bảo vệ, một phương tiện để giảm lỗi khi trừ hai số gần nhau. Các chữ số bảo vệ được IBM coi là đủ quan trọng nên vào năm 1968, hãng đã thêm một chữ số bảo vệ vào định dạng độ chính xác kép trong kiến ​​trúc Hệ thống/360 (độ chính xác đơn đã có một chữ số bảo vệ) và trang bị thêm cho tất cả các máy hiện có trong lĩnh vực này. Hai ví dụ được đưa ra để minh họa tiện ích của các chữ số bảo vệ

Tiêu chuẩn IEEE đi xa hơn là chỉ yêu cầu sử dụng chữ số bảo vệ. Nó cung cấp một thuật toán để cộng, trừ, nhân, chia và căn bậc hai, đồng thời yêu cầu việc triển khai tạo ra kết quả giống như thuật toán đó. Như vậy, khi một chương trình được chuyển từ máy này sang máy khác, kết quả của các thao tác cơ bản sẽ giống nhau đến từng bit nếu cả hai máy đều hỗ trợ chuẩn IEEE. Điều này đơn giản hóa rất nhiều việc chuyển các chương trình. Các cách sử dụng khác của thông số kỹ thuật chính xác này được đưa ra trong

Định dạng dấu phẩy động

Một số cách biểu diễn khác nhau của số thực đã được đề xuất, nhưng cho đến nay cách biểu diễn được sử dụng rộng rãi nhất là biểu diễn dấu phẩy động. Biểu diễn dấu phẩy động có cơ sở

Nhân mảng javascript
(luôn được coi là chẵn) và độ chính xác p. Nếu
Nhân mảng javascript
= 10 và p = 3 thì số 0. 1 được biểu diễn là 1. 00×10-1. Nếu
Nhân mảng javascript
= 2 và p = 24 thì số thập phân 0. 1 không thể được biểu diễn chính xác, nhưng xấp xỉ 1. 10011001100110011001101 × 2-4.

Nói chung, số dấu phẩy động sẽ được biểu diễn dưới dạng ± d. đ. d ×

Nhân mảng javascript
e, trong đó d. đ. d được gọi là có nghĩa và có p chữ số. Chính xác hơn ± d0. d1 d2. dp-1 ×
Nhân mảng javascript
e đại diện cho số

(1)

Thuật ngữ số dấu phẩy động sẽ được sử dụng để chỉ một số thực có thể được biểu diễn chính xác ở định dạng đang được thảo luận. Hai tham số khác liên quan đến biểu diễn dấu phẩy động là số mũ lớn nhất và nhỏ nhất được phép, emax và emin. Vì có

Nhân mảng javascript
p ý nghĩa có thể có và emax - emin + 1 số mũ có thể có, một số dấu phẩy động có thể được mã hóa trong

bit, trong đó +1 cuối cùng dành cho bit dấu. Mã hóa chính xác không quan trọng bây giờ

Có hai lý do tại sao một số thực có thể không được biểu diễn chính xác dưới dạng số dấu phẩy động. Tình huống phổ biến nhất được minh họa bằng số thập phân 0. 1. Mặc dù nó có dạng biểu diễn thập phân hữu hạn, nhưng ở dạng nhị phân, nó có dạng biểu diễn lặp lại vô hạn. Như vậy khi

Nhân mảng javascript
= 2 thì số 0. 1 nằm hoàn toàn giữa hai số dấu phẩy động và không thể biểu thị chính xác bởi cả hai số. Một tình huống ít phổ biến hơn là một số thực nằm ngoài phạm vi, nghĩa là giá trị tuyệt đối của nó lớn hơn
Nhân mảng javascript
×hoặc nhỏ hơn 1. 0 ×. Hầu hết bài viết này thảo luận về các vấn đề do nguyên nhân đầu tiên. Tuy nhiên, các số nằm ngoài phạm vi sẽ được thảo luận trong các phần và.

Các biểu diễn dấu phẩy động không nhất thiết phải là duy nhất. Ví dụ, cả 0. 01 × 101 và 1. 00 × 10-1 đại diện cho 0. 1. Nếu chữ số đầu khác không (d0

Nhân mảng javascript
0 trong phương trình trên), thì biểu diễn được gọi là chuẩn hóa. Số dấu phẩy động 1. 00 × 10-1 được chuẩn hóa, trong khi 0. 01 × 101 không phải. Khi
Nhân mảng javascript
 = 2, p = 3, emin = -1 và emax = 2 thì có 16 số dấu phẩy động chuẩn hóa, như minh họa trong. Các dấu thăng in đậm tương ứng với các số có nghĩa là 1. 00. Yêu cầu biểu diễn dấu phẩy động được chuẩn hóa làm cho biểu diễn trở nên độc nhất. Thật không may, hạn chế này làm cho nó không thể đại diện cho số không. Một cách tự nhiên để đại diện cho 0 là với 1. 0 ×, vì điều này bảo toàn thực tế là thứ tự số của các số thực không âm tương ứng với thứ tự từ điển của các biểu diễn dấu phẩy động của chúng. Khi số mũ được lưu trữ trong trường k bit, điều đó có nghĩa là chỉ có 2k - 1 giá trị khả dụng để sử dụng làm số mũ, vì một giá trị phải được dành riêng để biểu thị 0.

Lưu ý rằng × trong số dấu phẩy động là một phần của ký hiệu và khác với thao tác nhân dấu phẩy động. Ý nghĩa của biểu tượng × phải rõ ràng trong ngữ cảnh. Chẳng hạn, biểu thức (2. 5 × 10-3) × (4. 0 × 102) chỉ liên quan đến một phép nhân dấu phẩy động duy nhất

Nhân mảng javascript

HÌNH D-1 Số chuẩn hóa khi
Nhân mảng javascript
= 2, p = 3, emin = -1, emax = 2

Lỗi tương đối và Ulps

Vì lỗi làm tròn vốn có trong tính toán dấu phẩy động nên điều quan trọng là phải có cách đo lỗi này. Xem xét định dạng dấu phẩy động với

Nhân mảng javascript
 = 10 và p = 3, định dạng này sẽ được sử dụng trong suốt phần này. Nếu kết quả của phép tính dấu phẩy động là 3. 12 × 10-2, và đáp số khi được tính với độ chính xác vô hạn là. 0314, rõ ràng đây là lỗi của 2 đơn vị cuối cùng. Tương tự, nếu số thực. 0314159 được biểu thị bằng 3. 14×10-2 thì bị lỗi. 159 đơn vị ở vị trí cuối cùng. Nói chung, nếu số dấu phẩy động d. d. d ×
Nhân mảng javascript
e đại diện cho z thì nó bị lỗi bởi
Nhân mảng javascript
d. d. d - (z/
Nhân mảng javascript
e)
Nhân mảng javascript
Nhân mảng javascript
p-1 đơn vị ở vị trí cuối cùng. , Thuật ngữ ulps sẽ được sử dụng làm tốc ký cho "đơn vị ở vị trí cuối cùng. " Nếu kết quả của một phép tính là số dấu phẩy động gần nhất với kết quả chính xác, nó vẫn có thể bị lỗi nhiều như. 5 lần. Một cách khác để đo lường sự khác biệt giữa số dấu phẩy động và số thực mà nó xấp xỉ là lỗi tương đối, đơn giản là sự khác biệt giữa hai số chia cho số thực. Ví dụ: lỗi tương đối đã xảy ra khi xấp xỉ 3. 14159 nhân 3. 14 × 100 là. 00159/3. 14159 
Nhân mảng javascript
. 0005.

Để tính toán lỗi tương đối tương ứng với. 5 ulp, hãy quan sát rằng khi một số thực được xấp xỉ bằng số dấu phẩy động gần nhất có thể d. đ. dd ×

Nhân mảng javascript
e, lỗi có thể lớn bằng 0. 00. 00
Nhân mảng javascript
' ×
Nhân mảng javascript
e, trong đó
Nhân mảng javascript
' là chữ số
Nhân mảng javascript
/2, ở đó . Lỗi này là ((
Nhân mảng javascript
/2)
Nhân mảng javascript
-p) ×
Nhân mảng javascript
e. Vì các số có dạng d. đ. dd ×
Nhân mảng javascript
e đều có cùng một lỗi tuyệt đối, nhưng có các giá trị nằm trong khoảng từ
Nhân mảng javascript
e đến
Nhân mảng javascript
×
Nhân mảng javascript
e, the relative error ranges between ((
Nhân mảng javascript
/2)
Nhân mảng javascript
-p) ×
Nhân mảng javascript
e/
Nhân mảng javascript
e and ((
Nhân mảng javascript
/2)
Nhân mảng javascript
-p) ×
Nhân mảng javascript
e/
Nhân mảng javascript
e+1. That is,

(2)

Đặc biệt, lỗi tương đối tương ứng với. 5 ulp có thể thay đổi theo hệ số

Nhân mảng javascript
. Yếu tố này được gọi là dao động. Đặt
Nhân mảng javascript
= (
Nhân mảng javascript
/2)
Nhân mảng javascript
-p thành giới hạn lớn nhất trong số các giới hạn ở trên, chúng ta có thể nói rằng khi a .

Trong ví dụ trên, lỗi tương đối là. 00159/3. 14159

Nhân mảng javascript
. 0005. Để tránh những con số nhỏ như vậy, sai số tương đối thường được viết dưới dạng thừa số lần
Nhân mảng javascript
, trong trường hợp này là
Nhân mảng javascript
= (
Nhân mảng javascript
/2)
Nhân mảng javascript
-p = 5(10)-3 = .005. Thus the relative error would be expressed as (.00159/3.14159)/.005)
Nhân mảng javascript
Nhân mảng javascript
0. 1
Nhân mảng javascript
.

Để minh họa sự khác biệt giữa ulps và lỗi tương đối, hãy xem xét số thực x = 12. 35. Nó được xấp xỉ bởi = 1. 24 × 101. lỗi là 0. 5 ulps, lỗi tương đối là 0. 8

Nhân mảng javascript
. Tiếp theo xét phép tính 8. Giá trị chính xác là 8x = 98. 8, trong khi giá trị tính toán là 8= 9. 92 × 101. Lỗi bây giờ là 4. 0 ulps, nhưng lỗi tương đối vẫn là 0. 8
Nhân mảng javascript
. Lỗi được đo bằng ulps lớn hơn 8 lần, mặc dù lỗi tương đối là như nhau. Nói chung, khi cơ sở là
Nhân mảng javascript
, một lỗi tương đối cố định được biểu thị bằng ulps có thể dao động theo hệ số lên tới
Nhân mảng javascript
. Và ngược lại, như phương trình trên cho thấy, một sai số cố định của. 5 ulps dẫn đến một lỗi tương đối có thể dao động theo
Nhân mảng javascript
.

Cách tự nhiên nhất để đo lỗi làm tròn là trong ulps. Ví dụ: làm tròn đến số dấu phẩy động gần nhất tương ứng với lỗi nhỏ hơn hoặc bằng. 5 lần. Tuy nhiên, khi phân tích lỗi làm tròn do các công thức khác nhau gây ra, lỗi tương đối là biện pháp tốt hơn. Một minh họa tốt cho điều này là phân tích trong phần. Vì

Nhân mảng javascript
có thể đánh giá quá cao ảnh hưởng của việc làm tròn đến số dấu phẩy động gần nhất theo hệ số dao động của
Nhân mảng javascript
, ước tính lỗi của công thức sẽ chặt chẽ hơn trên các máy có kích thước nhỏ .
Nhân mảng javascript
.

Khi chỉ quan tâm đến thứ tự độ lớn của lỗi làm tròn, ulps và

Nhân mảng javascript
có thể được sử dụng thay thế cho nhau, vì chúng khác nhau tối đa theo hệ số
Nhân mảng javascript
. For example, when a floating-point number is in error by n ulps, that means that the number of contaminated digits is log
Nhân mảng javascript
n. Nếu lỗi tương đối trong phép tính là n
Nhân mảng javascript
thì

(3) chữ số bị ô nhiễm
Nhân mảng javascript
nhật ký
Nhân mảng javascript
n.

Số bảo vệ

Một phương pháp tính toán sự khác biệt giữa hai số dấu phẩy động là tính toán sự khác biệt một cách chính xác và sau đó làm tròn nó thành số dấu phẩy động gần nhất. Điều này rất tốn kém nếu các toán hạng khác nhau rất nhiều về kích thước. Giả sử p = 3, 2. 15×1012 - 1. 25 × 10-5 sẽ được tính là

x = 2. 15 × 1012
y =. 0000000000000000125 × 1012
x - y = 2. 1499999999999999875 × 1012

làm tròn thành 2. 15×1012. Thay vì sử dụng tất cả các chữ số này, phần cứng dấu phẩy động thường hoạt động trên một số chữ số cố định. Giả sử rằng số chữ số được giữ lại là p và khi toán hạng nhỏ hơn được dịch sang phải, các chữ số sẽ bị loại bỏ (trái ngược với làm tròn). Sau đó 2. 15 × 1012 - 1. 25 × 10-5 trở thành

x = 2. 15 × 1012
y = 0. 00 × 1012
x - y = 2. 15 × 1012

Câu trả lời giống hệt như thể sự khác biệt đã được tính toán chính xác và sau đó làm tròn. Lấy một ví dụ khác. 10. 1 - 9. 93. Điều này trở thành

x = 1. 01 × 101
y = 0. 99 × 101
x - y =. 02 × 101

Đáp án đúng là. 17, do đó, sự khác biệt được tính toán là 30 ulps và sai ở mỗi chữ số. Lỗi có thể tệ đến mức nào?

Định lý 1

Sử dụng định dạng dấu phẩy động có tham số
Nhân mảng javascript
và p, đồng thời tính toán sự khác biệt bằng cách sử dụng p chữ số, lỗi tương đối của kết quả có thể lớn bằng
Nhân mảng javascript
- 1.

Bằng chứng

Một lỗi tương đối của
Nhân mảng javascript
- 1 trong biểu thức x - y xảy ra khi x = 1. 00. 0 và y =.
Nhân mảng javascript
Nhân mảng javascript
.
Nhân mảng javascript
, trong đó
Nhân mảng javascript
=
Nhân mảng javascript
- 1. Ở đây y có p chữ số (tất cả bằng
Nhân mảng javascript
). Sự khác biệt chính xác là x - y =
Nhân mảng javascript
-p. Tuy nhiên, khi tính toán câu trả lời chỉ sử dụng p chữ số, chữ số ngoài cùng bên phải của y bị lệch đi và do đó, chênh lệch được tính toán là
Nhân mảng javascript
-p+1. Do đó, lỗi là
Nhân mảng javascript
-p -
Nhân mảng javascript
-p+1 =
Nhân mảng javascript
-p (
Nhân mảng javascript
- 1), and the relative error is
Nhân mảng javascript
-p(
Nhân mảng javascript
- 1)/
Nhân mảng javascript
-p =
Nhân mảng javascript
- 1. z

Khi

Nhân mảng javascript
=2, lỗi tương đối có thể lớn bằng kết quả và khi
Nhân mảng javascript
=10, lỗi có thể lớn gấp 9 lần. Hay nói cách khác, khi
Nhân mảng javascript
=2, phương trình cho thấy số chữ số bị nhiễm là log2(1/
Nhân mảng javascript
) = log2(2p) = p. Tức là tất cả các chữ số p trong kết quả đều sai. Giả sử rằng một chữ số bổ sung được thêm vào để bảo vệ chống lại tình huống này (một chữ số bảo vệ). Tức là số nhỏ hơn bị cắt bớt thành p + 1 chữ số, sau đó kết quả của phép trừ được làm tròn thành p chữ số. Với một chữ số bảo vệ, ví dụ trước trở thành

x = 1. 010 × 101
y = 0. 993 × 101
x - y =. 017 × 101

và câu trả lời là chính xác. Với một chữ số bảo vệ, sai số tương đối của kết quả có thể lớn hơn

Nhân mảng javascript
, như trong 110 - 8. 59.

x = 1. 10 × 102
y =. 085 × 102
x - y = 1. 015 × 102

Điều này làm tròn thành 102, so với câu trả lời đúng là 101. 41, cho một lỗi tương đối của. 006, lớn hơn

Nhân mảng javascript
=. 005. Nói chung, sai số tương đối của kết quả chỉ có thể lớn hơn một chút so với
Nhân mảng javascript
. Chính xác hơn,

Định lý 2

Nếu x và y là các số dấu phẩy động ở định dạng có tham số
Nhân mảng javascript
và p và nếu phép trừ được thực hiện với p + 1 chữ số (i. e. một chữ số bảo vệ), thì lỗi làm tròn tương đối trong kết quả nhỏ hơn 2
Nhân mảng javascript
.

Định lý này sẽ được chứng minh trong. Phép cộng được bao gồm trong định lý trên vì x và y có thể dương hoặc âm

hủy bỏ

Phần cuối cùng có thể được tóm tắt bằng cách nói rằng nếu không có số bảo vệ, sai số tương đối mắc phải khi trừ hai đại lượng gần nhau có thể rất lớn. Nói cách khác, việc đánh giá bất kỳ biểu thức nào chứa phép trừ (hoặc phép cộng các đại lượng trái dấu) có thể dẫn đến sai số tương đối lớn đến mức tất cả các chữ số đều vô nghĩa (Định lý 1). Khi trừ các đại lượng lân cận, các chữ số có nghĩa nhất trong toán hạng khớp và triệt tiêu lẫn nhau. Có hai loại hủy bỏ. thảm họa và lành tính

Hủy thảm khốc xảy ra khi các toán hạng có lỗi làm tròn. Ví dụ, trong công thức bậc hai, biểu thức b2 - 4ac xảy ra. Các đại lượng b2 và 4ac có thể bị lỗi làm tròn vì chúng là kết quả của phép nhân dấu phẩy động. Giả sử rằng chúng được làm tròn đến số dấu phẩy động gần nhất và do đó chính xác trong phạm vi. 5 lần. Khi chúng bị trừ, việc hủy bỏ có thể khiến nhiều chữ số chính xác biến mất, để lại các chữ số chủ yếu bị ô nhiễm do lỗi làm tròn. Do đó sự khác biệt có thể có lỗi của nhiều ulps. Ví dụ: xét b = 3. 34, a = 1. 22 và c = 2. 28. Giá trị chính xác của b2 - 4ac là. 0292. Nhưng b2 làm tròn thành 11. 2 và 4ac làm tròn thành 11. 1, do đó câu trả lời cuối cùng là. 1 là lỗi của 70 ulps, mặc dù 11. 2 - 11. 1 chính xác bằng. 1. Phép trừ không đưa ra bất kỳ lỗi nào, mà chỉ để lộ ra lỗi được đưa ra trong các phép nhân trước đó.

Xuất trừ lành tính xảy ra khi trừ chính xác các đại lượng đã biết. Nếu x và y không có sai số làm tròn thì theo Định lý 2 nếu phép trừ được thực hiện với một chữ số bảo vệ thì hiệu x-y có sai số tương đối rất nhỏ (nhỏ hơn 2

Nhân mảng javascript
).

Một công thức thể hiện sự hủy bỏ nghiêm trọng đôi khi có thể được sắp xếp lại để loại bỏ vấn đề. Lại xét công thức bậc hai

(4)
Nhân mảng javascript

Khi nào, sau đó không liên quan đến việc hủy bỏ và

.

Nhưng phép cộng (trừ) khác trong một trong các công thức sẽ bị hủy bỏ thảm khốc. Để tránh điều này, hãy nhân tử số và mẫu số của r1 với

(và tương tự cho r2) để có được

(5)
Nhân mảng javascript

Nếu và, thì tính toán r1 bằng công thức sẽ liên quan đến việc hủy bỏ. Do đó, hãy sử dụng công thức để tính r1 và cho r2. Mặt khác, nếu b < 0, sử dụng để tính r1 và cho r2.

Biểu thức x2 - y2 là một công thức khác thể hiện sự hủy bỏ thảm khốc. Sẽ chính xác hơn nếu đánh giá nó là (x - y)(x + y). Khác với căn thức bậc hai, dạng cải tiến này vẫn có phép trừ, nhưng là phép trừ đại lượng lành tính, không làm tròn sai, không thảm hại. Theo Định lý 2, sai số tương đối trong x - y tối đa là 2

Nhân mảng javascript
. Tương tự với x + y. Nhân hai đại lượng có sai số tương đối nhỏ sẽ tạo ra tích có sai số tương đối nhỏ (xem phần ).

Để tránh nhầm lẫn giữa giá trị chính xác và giá trị được tính toán, ký hiệu sau được sử dụng. Trong khi x - y biểu thị sự khác biệt chính xác của x và y, xy biểu thị sự khác biệt được tính toán (i. e. , với lỗi làm tròn). Tương tự như vậy

Nhân mảng javascript
,
Nhân mảng javascript
và lần lượt biểu thị phép tính cộng, nhân và chia. Tất cả các chữ hoa cho biết giá trị được tính toán của một hàm, như trong
     n = n/2
4 hoặc
     n = n/2
5. Các hàm chữ thường và ký hiệu toán học truyền thống biểu thị các giá trị chính xác của chúng như trong ln(x) và.

Mặc dù (xy)

Nhân mảng javascript
(x
Nhân mảng javascript
y) là một giá trị gần đúng tuyệt vời cho x2 - y2, nhưng các số dấu phẩy động x và y có thể chính là . Ví dụ: và có thể là các số thập phân đã biết chính xác không thể biểu thị chính xác ở dạng nhị phân. Trong trường hợp này, mặc dù x  y là một giá trị gần đúng tốt cho x - y, nhưng nó có thể có sai số tương đối lớn so với biểu thức đúng và do đó, lợi thế của (x + y)(x - y) so với x2 - y2 là . Vì tính toán (x + y)(x - y) có cùng khối lượng công việc như tính toán x2 - y2 nên rõ ràng đây là dạng được ưu tiên trong trường hợp này. Tuy nhiên, nói chung, việc thay thế một sự hủy bỏ nghiêm trọng bằng một sự hủy bỏ lành tính là không đáng nếu chi phí lớn, bởi vì đầu vào thường (nhưng không phải luôn luôn) là một phép tính gần đúng. Nhưng việc loại bỏ hoàn toàn một phép loại bỏ (như trong công thức bậc hai) là đáng giá ngay cả khi dữ liệu không chính xác. Xuyên suốt bài báo này, người ta sẽ giả định rằng các đầu vào dấu phẩy động cho một thuật toán là chính xác và kết quả được tính toán chính xác nhất có thể.

Biểu thức x2 - y2 chính xác hơn khi được viết lại thành (x - y)(x + y) vì một phép hủy thảm khốc được thay thế bằng một phép hủy lành tính. Tiếp theo, chúng tôi trình bày các ví dụ thú vị hơn về các công thức thể hiện sự hủy bỏ nghiêm trọng có thể được viết lại để chỉ biểu hiện sự hủy bỏ lành tính

Diện tích của một tam giác có thể được biểu thị trực tiếp theo độ dài của các cạnh a, b và c của nó như

(6)

(Giả sử tam giác rất phẳng; nghĩa là a

Nhân mảng javascript
b + c. Sau đó, s
Nhân mảng javascript
a và số hạng (s - a) trong công thức trừ hai số liền kề, một trong số đó có thể có lỗi làm tròn. Ví dụ: nếu a = 9. 0, b = c = 4. 53, giá trị đúng của s là 9. 03 và A là 2. 342. Mặc dù giá trị tính toán của s (9. 05) chỉ bị lỗi 2 ulps, giá trị tính toán của A là 3. 04, lỗi 70 ulps.

Có một cách để viết lại công thức sao cho nó sẽ trả về kết quả chính xác ngay cả đối với tam giác phẳng [Kahan 1986]. Nó là

(7)
Nhân mảng javascript

Nếu a, b và c không thỏa mãn a

Nhân mảng javascript
b
Nhân mảng javascript
c, đổi tên . Dễ dàng kiểm tra xem các vế phải của và có bằng nhau về mặt đại số không. Sử dụng các giá trị của a, b và c ở trên sẽ cho diện tích tính toán là 2. 35, sai 1 ulp và chính xác hơn nhiều so với công thức đầu tiên.

Mặc dù công thức chính xác hơn nhiều so với ví dụ này, nhưng thật tuyệt khi biết hiệu suất nói chung tốt như thế nào

Định lý 3

Sai số làm tròn phát sinh khi sử dụng để tính diện tích tam giác tối đa là 11
Nhân mảng javascript
, miễn là phép trừ được thực hiện với chữ số bảo vệ, e 
Nhân mảng javascript
 .005, and that square roots are computed to within 1/2 ulp.

Điều kiện e <. 005 được đáp ứng trong hầu hết mọi hệ thống dấu phẩy động thực tế. Ví dụ: khi = 2, p 8 đảm bảo rằng e <. 005 và khi = 10, p  3 là đủ.

Nhân mảng javascript
= 2, p
Nhân mảng javascript
8 ensures that e < .005, and when
Nhân mảng javascript
= 10, p 
Nhân mảng javascript
 3 is enough.

Trong các phát biểu như Định lý 3 thảo luận về sai số tương đối của một biểu thức, người ta hiểu rằng biểu thức được tính toán bằng cách sử dụng số học dấu phẩy động. Đặc biệt, lỗi tương đối thực sự là của biểu thức

(8)
     n = n/2
6((a
Nhân mảng javascript
(b
Nhân mảng javascript
c))
Nhân mảng javascript
(c(ab))
Nhân mảng javascript
(c
Nhân mảng javascript
(ab))
Nhân mảng javascript
(a
Nhân mảng javascript
(bc)))4

Do tính chất cồng kềnh của , trong phát biểu của các định lý, chúng ta thường nói giá trị tính toán của E thay vì viết ra E bằng ký hiệu vòng tròn

Giới hạn lỗi thường quá bi quan. Trong ví dụ số đưa ra ở trên, giá trị tính toán của là 2. 35, so với giá trị thực là 2. 34216 cho lỗi tương đối bằng 0. 7

Nhân mảng javascript
, nhỏ hơn nhiều so với 11
Nhân mảng javascript
. Lý do chính cho việc tính toán các giới hạn lỗi không phải là để có được các giới hạn chính xác mà là để xác minh rằng công thức không chứa các vấn đề về số.

Ví dụ cuối cùng về một biểu thức có thể được viết lại để sử dụng phép hủy lành tính là (1 + x)n, trong đó. Biểu thức này phát sinh trong tính toán tài chính. Cân nhắc gửi 100 đô la mỗi ngày vào tài khoản ngân hàng với lãi suất hàng năm là 6%, ghép lãi hàng ngày. Nếu n = 365 và i =. 06, số tiền tích lũy được vào cuối một năm là

100

đô la. Nếu giá trị này được tính bằng cách sử dụng

Nhân mảng javascript
= 2 và p = 24, thì kết quả là $37615. 45 so với câu trả lời chính xác là $37614. 05, chênh lệch $1. 40. Lý do của vấn đề là dễ dàng để xem. Biểu thức 1 + i/n liên quan đến việc thêm 1 vào. 0001643836, do đó các bit thứ tự thấp của i/n bị mất. Lỗi làm tròn này được khuếch đại khi 1 + i/n được nâng lên lũy thừa thứ n.

Biểu thức rắc rối (1 + i/n)n có thể được viết lại thành enln(1 + i/n), vấn đề bây giờ là tính ln(1 + x) cho x nhỏ. Một cách tiếp cận là sử dụng phép tính gần đúng ln(1 + x)

Nhân mảng javascript
x, trong trường hợp đó khoản thanh toán trở thành $37617. 26, giảm 3 đô la. 21 và thậm chí còn kém chính xác hơn công thức hiển nhiên. Nhưng có một cách để tính ln(1 + x) rất chính xác, như Định lý 4 cho thấy [Hewlett-Packard 1982]. Công thức này mang lại $37614. 07, chính xác trong vòng hai xu.

Định lý 4 giả định rằng

     n = n/2
4 xấp xỉ ln(x) trong phạm vi 1/2 ulp. Vấn đề nó giải quyết là khi x nhỏ,
     n = n/2
8(1
Nhân mảng javascript
x) không gần với ln(1 + x) vì 1
Nhân mảng javascript
x đã mất . Nghĩa là, giá trị tính toán của ln(1 + x) không gần với giá trị thực của nó khi.

Định lý 4

Nếu ln(1 + x) được tính theo công thức
Nhân mảng javascript

sai số tương đối nhiều nhất là 5
Nhân mảng javascript
khi 0
Nhân mảng javascript
x < 3/4, provided subtraction is performed with a guard digit, e < 0.1, and ln is computed to within 1/2 ulp.

Công thức này sẽ hoạt động với bất kỳ giá trị nào của x nhưng chỉ thú vị khi xảy ra sự hủy bỏ thảm khốc trong công thức ngây thơ ln(1 + x). Mặc dù công thức có vẻ bí ẩn, nhưng có một lời giải thích đơn giản cho lý do tại sao nó hoạt động. Viết ln(1 + x) dưới dạng

Nhân mảng javascript
.

Hệ số bên trái có thể được tính chính xác, nhưng hệ số bên phải µ(x) = ln(1 + x)/x sẽ bị lỗi làm tròn lớn khi thêm 1 vào x. Tuy nhiên, µ gần như không đổi, vì ln(1 + x)

Nhân mảng javascript
x. Vì vậy, thay đổi x một chút sẽ không gây ra nhiều lỗi. Nói cách khác, nếu, điện toán sẽ là một xấp xỉ tốt cho xµ(x) = ln(1 + x). Có giá trị nào có thể được tính toán chính xác không? .
Nhân mảng javascript
x)1, because then 1 +is exactly equal to 1
Nhân mảng javascript
x.

Kết quả của phần này có thể được tóm tắt bằng cách nói rằng một chữ số bảo vệ đảm bảo độ chính xác khi các đại lượng đã biết chính xác gần đó bị trừ đi (khử lành tính). Đôi khi một công thức đưa ra kết quả không chính xác có thể được viết lại để có độ chính xác số cao hơn nhiều bằng cách sử dụng phép hủy lành tính; . Giá của một chữ số bảo vệ không cao, bởi vì nó chỉ yêu cầu làm cho bộ cộng rộng hơn một chút. Đối với bộ cộng chính xác kép 54 bit, chi phí bổ sung nhỏ hơn 2%. Với mức giá này, bạn có thể chạy nhiều thuật toán, chẳng hạn như công thức tính diện tích hình tam giác và biểu thức ln(1 + x). Mặc dù hầu hết các máy tính hiện đại đều có số bảo vệ, nhưng có một số ít (chẳng hạn như hệ thống Cray) không có.

Hoạt động tròn chính xác

Khi các phép tính dấu phẩy động được thực hiện với một chữ số bảo vệ, chúng không chính xác như thể chúng được tính toán chính xác sau đó được làm tròn thành số dấu phẩy động gần nhất. Các hoạt động được thực hiện theo cách này sẽ được gọi là làm tròn chính xác. Ví dụ ngay trước Định lý 2 cho thấy một chữ số bảo vệ không phải lúc nào cũng cho kết quả được làm tròn chính xác. Phần trước đã đưa ra một số ví dụ về thuật toán yêu cầu chữ số bảo vệ để hoạt động bình thường. Phần này đưa ra các ví dụ về thuật toán yêu cầu làm tròn chính xác

Cho đến nay, định nghĩa về làm tròn vẫn chưa được đưa ra. Làm tròn đơn giản, ngoại trừ cách làm tròn nửa chừng các trường hợp; . 5 vòng thành 12 hay 13? . 5 sẽ làm tròn thành 13. Đây là cách làm tròn hoạt động trên máy tính VAX của Digital Equipment Corporation. Một trường phái tư duy khác nói rằng vì các số kết thúc bằng 5 nằm ở giữa hai lần làm tròn có thể, nên chúng nên làm tròn xuống một nửa và làm tròn nửa còn lại. Một cách để đạt được hành vi 50% này là yêu cầu kết quả được làm tròn có chữ số có nghĩa nhỏ nhất là số chẵn. Như vậy 12. 5 vòng thành 12 chứ không phải 13 vì 2 chẵn. Phương pháp nào trong số những phương pháp này là tốt nhất, làm tròn lên hoặc làm tròn thành số chẵn?

Định lý 5

Cho x và y là các số dấu phẩy động và xác định x0 = x, x1 = (x0y)
Nhân mảng javascript
y,. , xn = (xn-1  y)
Nhân mảng javascript
y. Nếu
Nhân mảng javascript
và được làm tròn chính xác bằng cách sử dụng làm tròn thành chẵn, thì xn = x với mọi n hoặc xn = x1 với mọi n
Nhân mảng javascript
1. z

Để làm rõ kết quả này, hãy xem xét

Nhân mảng javascript
= 10, p = 3 và đặt x = 1. 00, y = -. 555. Khi làm tròn lên, dãy trở thành

x0y = 1. 56, x1 = 1. 56. 555 = 1. 01, x1  y = 1. 01
Nhân mảng javascript
. 555 = 1. 57,

và mỗi giá trị liên tiếp của xn tăng thêm. 01, cho đến khi xn = 9. 45 (n

Nhân mảng javascript
845). Dưới tròn đến chẵn, xn luôn là 1. 00. Ví dụ này gợi ý rằng khi sử dụng quy tắc làm tròn số, các phép tính có thể tăng dần lên, trong khi khi sử dụng quy tắc làm tròn số chẵn, định lý nói rằng điều này không thể xảy ra. Trong suốt phần còn lại của bài viết này, làm tròn đến chẵn sẽ được sử dụng.

Một ứng dụng của làm tròn chính xác xảy ra trong nhiều số học chính xác. Có hai cách tiếp cận cơ bản để đạt được độ chính xác cao hơn. Một cách tiếp cận biểu thị các số dấu phẩy động bằng cách sử dụng một ý nghĩa rất lớn, được lưu trữ trong một mảng các từ và mã hóa các quy trình để thao tác các số này trong hợp ngữ. Cách tiếp cận thứ hai biểu thị các số dấu phẩy động có độ chính xác cao hơn dưới dạng một mảng các số dấu phẩy động thông thường, trong đó việc thêm các phần tử của mảng với độ chính xác vô hạn sẽ phục hồi số dấu phẩy động có độ chính xác cao. Đây là cách tiếp cận thứ hai sẽ được thảo luận ở đây. Ưu điểm của việc sử dụng một mảng các số dấu phẩy động là nó có thể được mã hóa khả chuyển bằng ngôn ngữ cấp cao, nhưng nó yêu cầu phép tính số học được làm tròn chính xác

Chìa khóa của phép nhân trong hệ thống này là biểu diễn tích xy dưới dạng tổng, trong đó mỗi phép cộng có cùng độ chính xác với x và y. Điều này có thể được thực hiện bằng cách tách x và y. Viết x = xh + xl và y = yh + yl, tích chính xác là

xy = xh yh + xh yl + xl yh + xl yl

Nếu x và y có p bit có nghĩa, thì các phép cộng cũng sẽ có p bit có nghĩa với điều kiện là xl, xh, yh, yl có thể được biểu diễn bằng cách sử dụng [p/2] bit. Khi p chẵn, dễ dàng tìm được một phép chia. số x0. x1. xp - 1 có thể được viết dưới dạng tổng của x0. x1. xp/2 - 1 và 0. 0. 0xp/2. xp - 1. Khi p là số lẻ, phương pháp tách đơn giản này sẽ không hoạt động. Tuy nhiên, có thể thu được thêm một bit bằng cách sử dụng các số âm. Ví dụ: nếu

Nhân mảng javascript
= 2, p = 5 và x =. 10111, x có thể được chia thành xh =. 11 và xl = -. 00001. Có nhiều hơn một cách để tách một số. Dekker [1971] đã đưa ra một phương pháp phân tách dễ tính toán, nhưng nó yêu cầu nhiều hơn một chữ số bảo vệ.

Định lý 6

Cho p là độ chính xác của dấu phẩy động, với hạn chế là p chẵn khi
Nhân mảng javascript
 > 2 và giả sử rằng các phép tính của dấu phẩy động được làm tròn chính xác. Sau đó, nếu k = [p/2] bằng một nửa độ chính xác (làm tròn lên) và m =
Nhân mảng javascript
k + 1, thì x có thể được chia thành x = xh + xl, trong đó xh = (m Nhân mảng javascript
 x)  (m
Nhân mảng javascript
xx), xl = xxh,

và mỗi xi có thể biểu diễn bằng cách sử dụng [p/2] bit chính xác

Để xem định lý này hoạt động như thế nào trong một ví dụ, hãy

Nhân mảng javascript
= 10, p = 4, b = 3. 476, một = 3. 463 và c = 3. 479. Sau đó, b2 - ac được làm tròn thành số dấu phẩy động gần nhất là. 03480, trong khi b
Nhân mảng javascript
b = 12. 08, a
Nhân mảng javascript
c = 12. 05, và do đó, giá trị tính toán của b2 - ac là. 03. Đây là lỗi 480 ulps. Sử dụng Định lý 6 để viết b = 3. 5 -. 024, a = 3. 5 -. 037 và c = 3. 5 -. 021, b2 trở thành 3. 52 - 2 × 3. 5 ×. 024 +. 0242. Mỗi mệnh đề đều chính xác, vì vậy b2 = 12. 25 -. 168 +. 000576, tại thời điểm này tổng vẫn chưa được đánh giá. Tương tự, ac = 3. 52 - (3. 5 ×. 037 + 3. 5 ×. 021) +. 037 ×. 021 = 12. 25 -. 2030 +. 000777. Cuối cùng, trừ hai chuỗi này theo số hạng sẽ đưa ra ước tính cho b2 - ac là 0 
Nhân mảng javascript
. 0350. 000201 =. 03480, giống với kết quả được làm tròn chính xác. Để chứng minh rằng Định lý 6 thực sự yêu cầu làm tròn chính xác, hãy xem xét p = 3,
Nhân mảng javascript
= 2 và x = 7. Khi đó m = 5, mx = 35 và m 
Nhân mảng javascript
 x = 32. Nếu phép trừ được thực hiện với một chữ số bảo vệ, thì (m 
Nhân mảng javascript
 x) x = 28. Do đó, xh = 4 và xl = 3, do đó xl không thể biểu diễn với [p/2] = 1 bit.

Ví dụ cuối cùng về làm tròn chính xác, xem xét chia m cho 10. Kết quả là một số dấu phẩy động nói chung sẽ không bằng m/10. Khi

Nhân mảng javascript
= 2, nhân m/10 với 10 sẽ khôi phục lại m, với điều kiện là sử dụng phương pháp làm tròn chính xác. Trên thực tế, một sự thật tổng quát hơn (do Kahan) là đúng. Bằng chứng là khéo léo, nhưng độc giả không quan tâm đến những chi tiết như vậy có thể bỏ qua phần trước.

Định lý 7

Khi
Nhân mảng javascript
= 2, nếu m và n là các số nguyên với. m. < 2p - 1 và n có dạng đặc biệt n = 2i + 2j, khi đó (mn)
Nhân mảng javascript
n = m, miễn là các phép toán dấu phẩy động được làm tròn chính xác.

Bằng chứng

Chia tỷ lệ theo lũy thừa hai là vô hại, vì nó chỉ thay đổi số mũ, không thay đổi ý nghĩa. Nếu q = m/n, thì chia tỷ lệ n sao cho 2p - 1
Nhân mảng javascript
n < 2p và chia tỷ lệ m sao cho 1/2 < q < 1. Do đó, 2p - 2 < m < 2p. Vì m có p bit có nghĩa nên nó có nhiều nhất một bit ở bên phải điểm nhị phân. Đổi dấu của m là vô hại, vì vậy giả sử rằng q > 0. Nếu = mn, để chứng minh định lý cần chứng minh rằng(9)
Đó là do m có nhiều nhất 1 bit bên phải điểm nhị phân nên n sẽ làm tròn thành m. Để đối phó với trường hợp giữa chừng khi. n-m. = 1/4, lưu ý rằng vì m chưa chia tỷ lệ ban đầu có. m. < 2p - 1, bit bậc thấp của nó là 0, vì vậy bit bậc thấp của m được chia tỷ lệ cũng là 0. Do đó, các trường hợp nửa chừng sẽ làm tròn thành m. Giả sử rằng q =. q1q2. , và để =. q1q2. qp1. Ước tính, ước lượng. n-m. , tính toán đầu tiên. - q. =. N/2p + 1 - m/n. ,
trong đó N là số nguyên lẻ. Vì n = 2i + 2j và 2p - 1
Nhân mảng javascript
n < 2p nên n = 2p - 1 + 2k đối với một số k
Nhân mảng javascript
p - 2, .
Nhân mảng javascript
.
Tử số là một số nguyên, và vì N là số lẻ nên thực tế nó là một số nguyên lẻ. Như vậy,. - q.
Nhân mảng javascript
1/(n2p + 1 - k).
Giả sử q
Nhân mảng javascript
Nhân mảng javascript

=(2p-1+2k)2-p-1-2-p-1 . z
This establishes and proves the theorem. z

Định lý đúng với mọi cơ số

Nhân mảng javascript
, miễn là 2i + 2j được thay thế bằng
Nhân mảng javascript
i +
Nhân mảng javascript
j. As
Nhân mảng javascript
lớn hơn, các mẫu số có dạng
Nhân mảng javascript
i +
Nhân mảng javascript
j ngày càng cách xa nhau.

Bây giờ chúng ta đang ở vị trí để trả lời câu hỏi, Có vấn đề gì không nếu các phép toán số học cơ bản gây ra lỗi làm tròn nhiều hơn một chút so với mức cần thiết? . Phần này đã thảo luận về một số thuật toán yêu cầu các chữ số bảo vệ để tạo ra kết quả chính xác theo nghĩa này. Tuy nhiên, nếu đầu vào của các công thức đó là các số đại diện cho các phép đo không chính xác, thì các giới hạn của Định lý 3 và 4 trở nên ít thú vị hơn. Lý do là sự hủy lành tính x - y có thể trở thành thảm họa nếu x và y chỉ xấp xỉ với một đại lượng đo được nào đó. Nhưng các phép toán chính xác rất hữu ích ngay cả khi đối mặt với dữ liệu không chính xác, bởi vì chúng cho phép chúng ta thiết lập các mối quan hệ chính xác như đã thảo luận trong Định lý 6 và 7. Chúng rất hữu ích ngay cả khi mọi biến dấu phẩy động chỉ là một giá trị gần đúng với một số giá trị thực

Tiêu chuẩn IEEE

Có hai tiêu chuẩn IEEE khác nhau cho tính toán dấu phẩy động. IEEE 754 là một tiêu chuẩn nhị phân yêu cầu

Nhân mảng javascript
= 2, p = 24 cho độ chính xác đơn và p = 53 cho độ chính xác kép [IEEE 1987]. Nó cũng chỉ định bố cục chính xác của các bit theo độ chính xác đơn và kép. IEEE 854 cho phép
Nhân mảng javascript
= 2 hoặc
Nhân mảng javascript
= 10 và không giống như 754, không chỉ định cách các số dấu phẩy động được mã hóa thành bit [Cody et al. 1984]. Nó không yêu cầu một giá trị cụ thể cho p, mà thay vào đó, nó chỉ định các ràng buộc đối với các giá trị cho phép của p đối với độ chính xác đơn và kép. Thuật ngữ Tiêu chuẩn IEEE sẽ được sử dụng khi thảo luận về các thuộc tính chung của cả hai tiêu chuẩn.

Phần này giới thiệu về tiêu chuẩn IEEE. Mỗi tiểu mục thảo luận về một khía cạnh của tiêu chuẩn và lý do tại sao nó được đưa vào. Mục đích của bài báo này không phải là để tranh luận rằng tiêu chuẩn IEEE là tiêu chuẩn dấu phẩy động tốt nhất có thể mà là để chấp nhận tiêu chuẩn như đã cho và giới thiệu về việc sử dụng nó. Để biết đầy đủ chi tiết, hãy tham khảo các tiêu chuẩn [IEEE 1987; . 1984]

Định dạng và hoạt động

Căn cứ

Rõ ràng là tại sao IEEE 854 cho phép

Nhân mảng javascript
= 10. Cơ số mười là cách con người trao đổi và suy nghĩ về các con số. Việc sử dụng
Nhân mảng javascript
= 10 đặc biệt phù hợp với máy tính bỏ túi, trong đó kết quả của mỗi thao tác được máy tính hiển thị ở dạng thập phân.

Có một số lý do tại sao IEEE 854 yêu cầu rằng nếu cơ sở không phải là 10 thì nó phải là 2. Phần này đã đề cập đến một lý do. kết quả phân tích lỗi chặt chẽ hơn nhiều khi

Nhân mảng javascript
là 2 do lỗi làm tròn của. 5 ulp dao động theo hệ số
Nhân mảng javascript
khi được tính là lỗi tương đối và phân tích lỗi hầu như luôn đơn giản hơn khi dựa trên lỗi tương đối. Một lý do liên quan phải làm với độ chính xác hiệu quả cho các cơ sở lớn. Xem xét
Nhân mảng javascript
= 16, p = 1 so với
Nhân mảng javascript
= 2, p = 4. Cả hai hệ thống đều có 4 bit có ý nghĩa. Coi bói bài 15/8. Khi
Nhân mảng javascript
= 2, 15 được biểu diễn bằng 1. 111 × 23 và 15/8 là 1. 111 × 20. Vậy 15/8 là chính xác. Tuy nhiên, khi
Nhân mảng javascript
= 16, 15 được biểu thị là F × 160, trong đó F là chữ số thập lục phân của 15. Nhưng 15/8 được biểu thị bằng 1 × 160, chỉ có một bit đúng. Nói chung, cơ sở 16 có thể mất tới 3 bit, do đó độ chính xác của p chữ số thập lục phân có thể có độ chính xác hiệu dụng thấp tới 4p - 3 thay vì 4p bit nhị phân. Vì các giá trị lớn của
Nhân mảng javascript
có những vấn đề này, tại sao IBM lại chọn
Nhân mảng javascript
= 16 cho hệ thống/370 của mình? . Đầu tiên là tăng phạm vi số mũ. Độ chính xác đơn trên hệ thống/370 có
Nhân mảng javascript
= 16, p = 6. Do đó, ý nghĩa yêu cầu 24 bit. Vì điều này phải phù hợp với 32 bit, điều này để lại 7 bit cho số mũ và một cho bit dấu. Do đó, độ lớn của các số có thể biểu thị nằm trong khoảng từ aboutto about=. Để có được phạm vi số mũ tương tự khi
Nhân mảng javascript
= 2 sẽ yêu cầu 9 bit số mũ, chỉ để lại 22 bit cho ý nghĩa và. Tuy nhiên, người ta chỉ ra rằng khi
Nhân mảng javascript
= 16, độ chính xác hiệu quả có thể thấp tới mức 4p - 3 = 21 bit. Thậm chí tệ hơn, khi
Nhân mảng javascript
= 2 có thể đạt được độ chính xác cao hơn một chút (như được giải thích sau trong phần này), do đó, máy
Nhân mảng javascript
= 2 có 23 .
Nhân mảng javascript
= 16 machine.

Một lời giải thích khả dĩ khác cho việc chọn

Nhân mảng javascript
= 16 liên quan đến việc dịch chuyển. Khi cộng hai số dấu phẩy động, nếu số mũ của chúng khác nhau, thì một trong các dấu phẩy sẽ phải dịch chuyển để làm cho các điểm cơ số thẳng hàng, làm chậm hoạt động. Trong hệ thống
Nhân mảng javascript
= 16, p = 1, tất cả các số từ 1 đến 15 đều có cùng số mũ, và do đó không cần dịch chuyển khi cộng bất kỳ () = 105 cặp số phân biệt có thể có . Tuy nhiên, trong hệ thống
Nhân mảng javascript
= 2, p = 4, những số này có số mũ nằm trong khoảng từ 0 đến 3 và cần phải dịch chuyển 70 trong số 105 cặp.

Trong hầu hết các phần cứng hiện đại, hiệu suất đạt được bằng cách tránh dịch chuyển cho một tập hợp con toán hạng là không đáng kể và do đó, độ dao động nhỏ của

Nhân mảng javascript
= 2 làm cho nó trở thành cơ sở thích hợp hơn. Một ưu điểm khác của việc sử dụng
Nhân mảng javascript
= 2 là có một cách để đạt được thêm một chút ý nghĩa. Vì các số dấu phẩy động luôn được chuẩn hóa, nên bit quan trọng nhất của ý nghĩa luôn là 1 và không có lý do gì để lãng phí một chút dung lượng lưu trữ để biểu thị nó. Các định dạng sử dụng thủ thuật này được cho là có một bit ẩn. Người ta đã chỉ ra rằng điều này đòi hỏi một quy ước đặc biệt cho 0. Phương pháp được đưa ra ở đó là một số mũ của emin - 1 và một ý nghĩa của tất cả các số 0 không đại diện, mà là 0.

Độ chính xác đơn của IEEE 754 được mã hóa thành 32 bit, sử dụng 1 bit cho dấu, 8 bit cho số mũ và 23 bit cho dấu và. Tuy nhiên, nó sử dụng một bit ẩn, do đó, ý nghĩa là 24 bit (p = 24), mặc dù nó được mã hóa chỉ bằng 23 bit

Độ chính xác

Tiêu chuẩn IEEE xác định bốn độ chính xác khác nhau. đơn, đôi, mở rộng đơn và mở rộng gấp đôi. Trong IEEE 754, độ chính xác đơn và kép tương ứng với hầu hết các phần cứng dấu phẩy động cung cấp. Độ chính xác đơn chiếm một từ 32 bit duy nhất, độ chính xác kép chiếm hai từ 32 bit liên tiếp. Độ chính xác mở rộng là một định dạng cung cấp thêm ít nhất một chút độ chính xác và phạm vi số mũ ()

BẢNG D-1   Thông số định dạng IEEE 754SingleSingle-ExtendedDoubleDouble-Extendedp24
Nhân mảng javascript
3253
Nhân mảng javascript
64emax+127
Nhân mảng javascript
1023+
Nhân mảng javascript
-1022-1022
Nhân mảng javascript
-16382Exponent width in bits8
Nhân mảng javascript
1111
Nhân mảng javascript
15Format width in bits32
Nhân mảng javascript
4364
Nhân mảng javascript
79

Tiêu chuẩn IEEE chỉ xác định giới hạn dưới về số lượng bit bổ sung mà độ chính xác mở rộng cung cấp. Định dạng mở rộng kép tối thiểu được phép đôi khi được gọi là định dạng 80 bit, mặc dù bảng hiển thị định dạng đó bằng cách sử dụng 79 bit. Lý do là việc triển khai phần cứng với độ chính xác mở rộng thường không sử dụng bit ẩn và do đó sẽ sử dụng 80 thay vì 79 bit

Tiêu chuẩn nhấn mạnh nhất vào độ chính xác mở rộng, không đưa ra khuyến nghị nào liên quan đến độ chính xác kép, nhưng khuyến nghị mạnh mẽ rằng Triển khai nên hỗ trợ định dạng mở rộng tương ứng với định dạng cơ bản rộng nhất được hỗ trợ,

Một động lực cho độ chính xác mở rộng đến từ máy tính, thường sẽ hiển thị 10 chữ số, nhưng sử dụng 13 chữ số bên trong. Bằng cách chỉ hiển thị 10 trong số 13 chữ số, máy tính xuất hiện với người dùng dưới dạng "hộp đen" tính toán hàm mũ, cosin, v.v. chính xác đến 10 chữ số. Để máy tính tính toán các hàm như exp, log và cos trong vòng 10 chữ số với hiệu quả hợp lý, nó cần thêm một vài chữ số để hoạt động với. Không khó để tìm một biểu thức hữu tỷ đơn giản xấp xỉ nhật ký với sai số 500 đơn vị ở vị trí cuối cùng. Do đó tính toán với 13 chữ số cho câu trả lời đúng với 10 chữ số. Bằng cách ẩn 3 chữ số thừa này, máy tính đưa ra một mô hình đơn giản cho người vận hành

Độ chính xác mở rộng trong tiêu chuẩn IEEE phục vụ chức năng tương tự. Nó cho phép các thư viện tính toán số lượng một cách hiệu quả trong khoảng. 5 ulp với độ chính xác đơn (hoặc kép), cung cấp cho người dùng các thư viện đó một mô hình đơn giản, cụ thể là mỗi thao tác nguyên thủy, có thể là một phép nhân đơn giản hoặc một lệnh gọi nhật ký, trả về một giá trị chính xác trong khoảng. 5 lần. Tuy nhiên, khi sử dụng độ chính xác mở rộng, điều quan trọng là đảm bảo rằng việc sử dụng nó minh bạch cho người dùng. Ví dụ: trên máy tính bỏ túi, nếu biểu diễn bên trong của giá trị hiển thị không được làm tròn đến cùng độ chính xác như hiển thị, thì kết quả của các thao tác tiếp theo sẽ phụ thuộc vào các chữ số bị ẩn và có vẻ như người dùng không thể đoán trước

Để minh họa thêm về độ chính xác mở rộng, hãy xem xét vấn đề chuyển đổi giữa độ chính xác đơn và số thập phân của IEEE 754. Lý tưởng nhất là các số có độ chính xác đơn sẽ được in với đủ chữ số để khi đọc lại số thập phân, số có độ chính xác đơn có thể được phục hồi. Hóa ra 9 chữ số thập phân là đủ để khôi phục một số nhị phân chính xác duy nhất (xem phần ). Khi chuyển đổi một số thập phân trở lại biểu diễn nhị phân duy nhất của nó, một lỗi làm tròn nhỏ bằng 1 ulp là nghiêm trọng, vì nó sẽ đưa ra câu trả lời sai. Đây là một tình huống mà độ chính xác mở rộng là rất quan trọng đối với một thuật toán hiệu quả. Khi khả dụng mở rộng đơn, tồn tại một phương pháp rất đơn giản để chuyển đổi một số thập phân thành một số nhị phân chính xác duy nhất. Đầu tiên đọc trong 9 chữ số thập phân dưới dạng số nguyên N, bỏ qua dấu thập phân. Từ , p 

Nhân mảng javascript
 32 và từ 109 
Nhân mảng javascript
4. 3 × 109, N có thể được biểu diễn chính xác ở dạng mở rộng đơn. Tiếp theo, tìm công suất thích hợp 10P cần thiết để mở rộng quy mô N. Đây sẽ là sự kết hợp của số mũ của số thập phân, cùng với vị trí của dấu thập phân bị bỏ qua (cho đến bây giờ). Tin học 10. P. Nếu. P.
Nhân mảng javascript
 13, thì giá trị này cũng được biểu thị chính xác, vì 1013 = 213513 và 513 
Nhân mảng javascript
13, việc sử dụng định dạng mở rộng một lần cho phép chuyển đổi các số thập phân gồm 9 chữ số thành số nhị phân gần nhất (i. e. làm tròn chính xác). Nếu. P. > 13, thì mở rộng một lần là không đủ để thuật toán trên luôn tính toán số nhị phân tương đương được làm tròn chính xác, nhưng Coonen [1984] chỉ ra rằng nó đủ để đảm bảo rằng việc chuyển đổi nhị phân sang thập phân và ngược lại sẽ phục hồi số nhị phân ban đầu.

Nếu độ chính xác kép được hỗ trợ, thì thuật toán ở trên sẽ được chạy ở độ chính xác kép thay vì mở rộng đơn, nhưng để chuyển đổi độ chính xác kép thành số thập phân 17 chữ số và ngược lại sẽ yêu cầu định dạng mở rộng kép

số mũ

Vì số mũ có thể dương hoặc âm nên phải chọn phương pháp nào đó để biểu diễn dấu của nó. Hai phương pháp phổ biến để biểu diễn các số có dấu là dấu/độ lớn và phần bù hai. Dấu hiệu/độ lớn là hệ thống được sử dụng cho dấu hiệu của ý nghĩa và trong các định dạng IEEE. một bit được sử dụng để giữ dấu, các bit còn lại đại diện cho độ lớn của số. Biểu diễn bổ sung của hai thường được sử dụng trong số học số nguyên. Trong lược đồ này, một số trong phạm vi [-2p-1, 2p-1 - 1] được biểu diễn bằng số không âm nhỏ nhất đồng dư với nó theo modulo 2p

Tiêu chuẩn nhị phân của IEEE không sử dụng một trong hai phương pháp này để biểu diễn số mũ mà thay vào đó sử dụng biểu diễn sai lệch. Trong trường hợp độ chính xác đơn, trong đó số mũ được lưu trữ trong 8 bit, độ lệch là 127 (đối với độ chính xác kép là 1023). Điều này có nghĩa là nếu giá trị của các bit lũy thừa được hiểu là một số nguyên không dấu, thì số mũ của số dấu phẩy động là- 127. Đây thường được gọi là số mũ không thiên vị để phân biệt với số mũ thiên vị

Nhắc đến , độ chính xác đơn có emax = 127 và emin = -126. Lý do để có. emin. < emax sao cho nghịch đảo của số nhỏ nhất không bị tràn. Mặc dù đúng là số nghịch đảo của số lớn nhất sẽ tràn, nhưng tràn thường ít nghiêm trọng hơn tràn. Phần này giải thích rằng emin - 1 được sử dụng để đại diện cho 0 và sẽ giới thiệu cách sử dụng emax + 1. Trong độ chính xác đơn của IEEE, điều này có nghĩa là các số mũ sai lệch nằm trong khoảng từ emin - 1 = -127 và emax + 1 = 128, trong khi các số mũ không thiên vị nằm trong khoảng từ 0 đến 255, chính xác là các số không âm có thể được biểu diễn bằng 8 bit

hoạt động

Tiêu chuẩn IEEE yêu cầu kết quả của phép cộng, phép trừ, phép nhân và phép chia phải được làm tròn chính xác. Nghĩa là, kết quả phải được tính toán chính xác và sau đó làm tròn đến số dấu phẩy động gần nhất (sử dụng làm tròn số chẵn). Phần này đã chỉ ra rằng việc tính toán hiệu hoặc tổng chính xác của hai số dấu phẩy động có thể rất tốn kém khi số mũ của chúng khác nhau đáng kể. Phần đó đã giới thiệu các chữ số bảo vệ, cung cấp một cách thực tế để tính toán sự khác biệt trong khi vẫn đảm bảo rằng sai số tương đối là nhỏ. Tuy nhiên, tính toán với một chữ số bảo vệ sẽ không phải lúc nào cũng đưa ra câu trả lời giống như tính toán kết quả chính xác và sau đó làm tròn. Bằng cách đưa ra một chữ số bảo vệ thứ hai và một bit cố định thứ ba, sự khác biệt có thể được tính toán với chi phí chỉ cao hơn một chút so với một chữ số bảo vệ duy nhất, nhưng kết quả vẫn giống như nếu sự khác biệt được tính toán chính xác và sau đó được làm tròn [Goldberg 1990]. Do đó, tiêu chuẩn có thể được thực hiện một cách hiệu quả

Một lý do để xác định hoàn toàn kết quả của các phép tính số học là để cải thiện tính di động của phần mềm. Khi một chương trình được di chuyển giữa hai máy và cả hai đều hỗ trợ số học IEEE, thì nếu có bất kỳ kết quả trung gian nào khác đi, thì đó phải là do lỗi phần mềm, không phải do sự khác biệt về số học. Một ưu điểm khác của đặc tả chính xác là nó giúp dễ dàng suy luận về dấu chấm động hơn. Chứng minh về dấu phẩy động đủ khó, không cần phải xử lý nhiều trường hợp phát sinh từ nhiều loại số học. Giống như các chương trình số nguyên có thể được chứng minh là đúng, các chương trình dấu phẩy động cũng vậy, mặc dù điều được chứng minh trong trường hợp đó là lỗi làm tròn của kết quả thỏa mãn các giới hạn nhất định. Định lý 4 là một ví dụ về chứng minh như vậy. Những bằng chứng này được thực hiện dễ dàng hơn nhiều khi các hoạt động được suy luận được chỉ định chính xác. Sau khi một thuật toán được chứng minh là đúng cho số học IEEE, nó sẽ hoạt động chính xác trên bất kỳ máy nào hỗ trợ tiêu chuẩn IEEE

Brown [1981] đã đề xuất các tiên đề cho dấu phẩy động bao gồm hầu hết phần cứng dấu phẩy động hiện có. Tuy nhiên, bằng chứng trong hệ thống này không thể xác minh thuật toán của các phần và , yêu cầu các tính năng không có trên tất cả phần cứng. Hơn nữa, các tiên đề của Brown phức tạp hơn việc chỉ đơn giản xác định các thao tác được thực hiện chính xác và sau đó làm tròn. Do đó, việc chứng minh các định lý từ các tiên đề Brown thường khó hơn việc chứng minh chúng với giả định các phép toán được làm tròn chính xác

Không có thỏa thuận hoàn toàn về những hoạt động mà một tiêu chuẩn dấu phẩy động sẽ bao gồm. Ngoài các phép toán cơ bản +, -, × và /, tiêu chuẩn IEEE cũng chỉ định rằng căn bậc hai, phần dư và chuyển đổi giữa số nguyên và dấu phẩy động được làm tròn chính xác. Nó cũng yêu cầu chuyển đổi giữa các định dạng bên trong và số thập phân được làm tròn chính xác (ngoại trừ các số rất lớn). Kulisch và Miranker [1986] đã đề xuất thêm sản phẩm bên trong vào danh sách các hoạt động được chỉ định chính xác. Họ lưu ý rằng khi các sản phẩm bên trong được tính toán theo số học của IEEE, câu trả lời cuối cùng có thể khá sai. Ví dụ: tổng là trường hợp đặc biệt của tích bên trong và tổng ((2 × 10-30 + 1030) - 1030) - 10-30 chính xác bằng 10-30, nhưng trên máy có số học IEEE, kết quả tính toán sẽ . Có thể tính toán các sản phẩm bên trong trong vòng 1 ulp với ít phần cứng hơn so với việc triển khai hệ số nhân nhanh [Kirchner và Kulish 1987].

Tất cả các hoạt động được đề cập trong tiêu chuẩn được yêu cầu phải được làm tròn chính xác ngoại trừ chuyển đổi giữa thập phân và nhị phân. Lý do là các thuật toán hiệu quả để làm tròn chính xác tất cả các phép toán đã biết, ngoại trừ chuyển đổi. Để chuyển đổi, các thuật toán hiệu quả nhất được biết đến tạo ra kết quả kém hơn một chút so với các thuật toán được làm tròn chính xác [Coonen 1984]

Tiêu chuẩn IEEE không yêu cầu các hàm siêu việt phải được làm tròn chính xác do tình trạng tiến thoái lưỡng nan của người tạo bảng. Để minh họa, giả sử bạn đang tạo một bảng hàm mũ tới 4 vị trí. Sau đó, hết hạn (1. 626) = 5. 0835. Điều này có nên được làm tròn thành 5. 083 hoặc 5. 084? . 626) được tính toán cẩn thận hơn, nó trở thành 5. 08350. Và sau đó 5. 083500. Và sau đó 5. 0835000. Vì exp là siêu nghiệm, nên điều này có thể tiếp diễn tùy ý trong một khoảng thời gian dài trước khi phân biệt được liệu exp(1. 626) là 5. 083500. 0ddd hoặc 5. 0834999. 9ddd. Do đó, không thực tế khi xác định rằng độ chính xác của các hàm siêu việt giống như khi chúng được tính toán với độ chính xác vô hạn và sau đó được làm tròn. Một cách tiếp cận khác là chỉ định các hàm siêu việt theo thuật toán. Nhưng dường như không có một thuật toán nào hoạt động tốt trên tất cả các kiến ​​trúc phần cứng. Xấp xỉ hợp lý, CORDIC và bảng lớn là ba kỹ thuật khác nhau được sử dụng để tính toán siêu việt trên các máy hiện đại. Mỗi cái phù hợp với một loại phần cứng khác nhau và hiện tại không có thuật toán đơn lẻ nào hoạt động được chấp nhận trên nhiều loại phần cứng hiện tại

Số lượng đặc biệt

Trên một số phần cứng dấu phẩy động, mọi mẫu bit biểu thị một số dấu phẩy động hợp lệ. Hệ thống IBM/370 là một ví dụ về điều này. Mặt khác, VAXTM dành riêng một số mẫu bit để biểu thị các số đặc biệt được gọi là toán hạng dành riêng. Ý tưởng này bắt nguồn từ CDC 6600, có các mẫu bit cho số lượng đặc biệt

     n = n/2
9 và
     if (n==0) return u
0

Tiêu chuẩn IEEE tiếp tục theo truyền thống này và có NaN (Không phải là Số) và vô số. Không có bất kỳ đại lượng đặc biệt nào, không có cách nào tốt để xử lý các tình huống đặc biệt như lấy căn bậc hai của một số âm, ngoài việc hủy bỏ tính toán. Trong Hệ thống IBM/370 FORTRAN, hành động mặc định để đáp ứng việc tính căn bậc hai của một số âm như -4 dẫn đến việc in thông báo lỗi. Vì mỗi mẫu bit đại diện cho một số hợp lệ, nên giá trị trả về của căn bậc hai phải là một số dấu phẩy động. Trong trường hợp Hệ thống/370 FORTRAN, được trả về. Trong số học của IEEE, một NaN được trả về trong tình huống này

Tiêu chuẩn IEEE chỉ định các giá trị đặc biệt sau (xem ). ± 0, số không chuẩn hóa, ±

Nhân mảng javascript
và NaN (có nhiều hơn một NaN, như được giải thích trong phần tiếp theo). Tất cả các giá trị đặc biệt này đều được mã hóa bằng số mũ của emax + 1 hoặc emin - 1 (đã chỉ ra rằng 0 có số mũ là emin - 1).

BẢNG D-2   IEEE 754 Giá trị Đặc biệt Số mũPhân sốĐại diện = emin - 1f = 0±0e = emin - 1f
Nhân mảng javascript
0emin
Nhân mảng javascript
e
Nhân mảng javascript
emax--1.f × 2ee = emax + 1f = 0±
Nhân mảng javascript
e = emax + 1f
Nhân mảng javascript
0NaN

NaN

Theo truyền thống, phép tính 0/0 hoặc đã được coi là một lỗi không thể khôi phục khiến quá trình tính toán bị dừng. Tuy nhiên, có những ví dụ hợp lý để tính toán tiếp tục trong tình huống như vậy. Hãy xem xét một chương trình con tìm các số 0 của một hàm f, chẳng hạn như

     if (n==0) return u
1. Theo truyền thống, công cụ tìm số 0 yêu cầu người dùng nhập khoảng [a, b] mà hàm được xác định và trên đó công cụ tìm số 0 sẽ tìm kiếm. Đó là, chương trình con được gọi là
     if (n==0) return u
2,
     if (n==0) return u
3,
     if (n==0) return u
4. Công cụ tìm số 0 hữu ích hơn sẽ không yêu cầu người dùng nhập thông tin bổ sung này. Công cụ tìm số 0 tổng quát hơn này đặc biệt thích hợp cho máy tính, trong đó việc nhập một hàm là điều tự nhiên và sau đó rất khó xử khi phải chỉ định miền. Tuy nhiên, thật dễ hiểu tại sao hầu hết các công cụ tìm số 0 đều yêu cầu miền. Công cụ tìm số 0 thực hiện công việc của nó bằng cách thăm dò hàm
     if (n==0) return u
5 ở các giá trị khác nhau. Nếu nó thăm dò một giá trị bên ngoài miền của
     if (n==0) return u
5, thì mã của
     if (n==0) return u
5 có thể tính toán tốt 0/0 hoặc, và quá trình tính toán sẽ dừng lại, hủy bỏ quá trình tìm số 0 một cách không cần thiết

Vấn đề này có thể tránh được bằng cách đưa ra một giá trị đặc biệt gọi là NaN và chỉ định rằng việc tính toán các biểu thức như 0/0 và tạo ra NaN, thay vì tạm dừng. Danh sách một số tình huống có thể gây ra NaN được đưa ra trong. Sau đó, khi

     if (n==0) return u
1 thăm dò bên ngoài miền của
     if (n==0) return u
5, mã cho
     if (n==0) return u
5 sẽ trả về NaN và công cụ tìm số 0 có thể tiếp tục. Tức là,
     if (n==0) return u
1 không bị "trừng phạt" vì đoán sai. Với ví dụ này, thật dễ dàng để biết kết quả của việc kết hợp NaN với một số dấu phẩy động thông thường sẽ là gì. Giả sử rằng tuyên bố cuối cùng của
     if (n==0) return u
5 là
     n = n/2
13
     n = n/2
14. Nếu d < 0, thì
     if (n==0) return u
5 sẽ trả về một NaN. Vì d < 0, nên
     n = n/2
16 là một NaN và
     n = n/2
17 sẽ là một NaN, nếu tổng của một NaN và bất kỳ số nào khác là một NaN. Tương tự, nếu một toán hạng của phép chia là NaN, thì thương phải là NaN. Nói chung, bất cứ khi nào một NaN tham gia vào hoạt động dấu chấm động, kết quả là một NaN khác

BẢNG D-3   Các thao tác tạo ra NaN+
Nhân mảng javascript
+ (-
Nhân mảng javascript
)×0 ×
Nhân mảng javascript
/0
Nhân mảng javascript
/
Nhân mảng javascript
     n = n/2
18x
     n = n/2
18 0,
Nhân mảng javascript
     n = n/2
18 y(when x < 0)

Một cách tiếp cận khác để viết bộ giải số 0 không yêu cầu người dùng nhập miền là sử dụng tín hiệu. Trình tìm điểm không có thể cài đặt trình xử lý tín hiệu cho các ngoại lệ dấu phẩy động. Sau đó, nếu

     if (n==0) return u
5 được đánh giá bên ngoài miền của nó và đưa ra một ngoại lệ, quyền kiểm soát sẽ được trả lại cho bộ giải zero. Vấn đề với cách tiếp cận này là mỗi ngôn ngữ có một phương thức xử lý tín hiệu khác nhau (nếu nó có một phương thức nào đó), và vì vậy nó không có hy vọng về tính di động

Trong IEEE 754, NaN thường được biểu diễn dưới dạng số dấu phẩy động với số mũ emax + 1 và các ký hiệu khác không. Việc triển khai được tự do đưa thông tin phụ thuộc vào hệ thống vào ý nghĩa và. Do đó, không có một NaN duy nhất, mà là cả một gia đình NaN. Khi một NaN và một số dấu phẩy động thông thường được kết hợp, kết quả sẽ giống với toán hạng NaN. Do đó, nếu kết quả của một phép tính dài là NaN, thì thông tin phụ thuộc vào hệ thống trong ý nghĩa và sẽ là thông tin được tạo khi NaN đầu tiên trong phép tính được tạo. Trên thực tế, có một báo trước cho tuyên bố cuối cùng. Nếu cả hai toán hạng đều là NaN, thì kết quả sẽ là một trong những NaN đó, nhưng đó có thể không phải là NaN được tạo trước

vô cực

Giống như NaN cung cấp một cách để tiếp tục tính toán khi gặp phải các biểu thức như 0/0 ora, infinity cung cấp một cách để tiếp tục khi xảy ra tràn. Điều này an toàn hơn nhiều so với việc chỉ trả về số lớn nhất có thể biểu thị. Ví dụ: xem xét máy tính, khi

Nhân mảng javascript
 = 10, p = 3 và emax = 98. Nếu x = 3 × 1070 và y = 4 × 1070 thì x2 sẽ tràn và được thay thế bằng 9. 99 × 1098. Tương tự, y2 và x2 + y2 sẽ lần lượt tràn và được thay thế bằng 9. 99 × 1098. Vì vậy, kết quả cuối cùng sẽ là, đó là sai nghiêm trọng. câu trả lời đúng là 5 × 1070. Trong số học IEEE, kết quả của x2 là
Nhân mảng javascript
, cũng như y2, x2 + y2 và. Vì vậy, kết quả cuối cùng là
Nhân mảng javascript
, sẽ an toàn hơn so với việc trả về một số dấu phẩy động thông thường không ở gần câu trả lời đúng.

Việc chia 0 cho 0 dẫn đến một NaN. Tuy nhiên, một số khác không chia cho 0, trả về vô cùng. 1/0 =

Nhân mảng javascript
, -1/0 = -
Nhân mảng javascript
. Lý do cho sự khác biệt này là. nếu f(x)
Nhân mảng javascript
0 và g(x)
Nhân mảng javascript
0 khi x tiến đến một giới hạn nào đó, thì f(x)/g(x) có thể có bất kỳ giá trị nào. Ví dụ: khi f(x) = sin x và g(x) = x thì f(x)/g(x)
Nhân mảng javascript
1 là x
Nhân mảng javascript
. Nhưng khi f(x) = 1 - cos x, f(x)/g(x)
Nhân mảng javascript
0. Khi coi 0/0 là tình huống giới hạn của thương của hai số rất nhỏ, 0/0 có thể đại diện cho bất kỳ thứ gì. Do đó, trong tiêu chuẩn IEEE, 0/0 dẫn đến một NaN. Nhưng khi c > 0, f(x)
Nhân mảng javascript
c và g(x)
Nhân mảng javascript
0 thì f(x)/g(x) 
Nhân mảng javascript
 ±
Nhân mảng javascript
, for any analytic functions f and g. If g(x) < 0 for small x, then f(x)/g(x)
Nhân mảng javascript
-
Nhân mảng javascript
, ngược lại giới hạn là +
Nhân mảng javascript
. So the IEEE standard defines c/0 = ±
Nhân mảng javascript
, miễn là c
Nhân mảng javascript
0. Dấu của
Nhân mảng javascript
phụ thuộc vào dấu của c và 0 theo cách thông thường, sao cho -10/0 = -
Nhân mảng javascript
và -10/-0 = . Bạn có thể phân biệt giữa nhận
Nhân mảng javascript
. You can distinguish between getting
Nhân mảng javascript
do tràn và nhận
Nhân mảng javascript
do chia cho 0 bằng cách kiểm tra các cờ trạng thái (sẽ được thảo luận chi tiết trong phần ). Cờ tràn sẽ được đặt trong trường hợp đầu tiên, cờ chia cho 0 trong trường hợp thứ hai.

Quy tắc xác định kết quả của phép toán có toán hạng là vô hạn rất đơn giản. thay số vô cực bằng số hữu hạn x và lấy giới hạn là x

Nhân mảng javascript
Nhân mảng javascript
. Do đó 3/
Nhân mảng javascript
 = 0, vì

.

Tương tự, 4 -

Nhân mảng javascript
= -
Nhân mảng javascript
và = 
Nhân mảng javascript
. Khi giới hạn không tồn tại, kết quả sẽ là NaN, vì vậy
Nhân mảng javascript
/
Nhân mảng javascript
sẽ là NaN ( có ví dụ bổ sung). Điều này đồng ý với lý do được sử dụng để kết luận rằng 0/0 phải là NaN.

Khi một biểu thức con ước tính thành NaN, giá trị của toàn bộ biểu thức cũng là một NaN. Tuy nhiên, trong trường hợp ±

Nhân mảng javascript
, giá trị của biểu thức có thể là một số dấu phẩy động thông thường do các quy tắc như 1/
Nhân mảng javascript
= 0. Đây là một ví dụ thực tế sử dụng các quy tắc cho số học vô cực. Xét tính hàm x/(x2 + 1). Đây là một công thức tồi, bởi vì nó không chỉ tràn khi x lớn hơn, mà số học vô cực sẽ đưa ra câu trả lời sai vì nó sẽ mang lại 0, thay vì một số gần 1/x. Tuy nhiên, x/(x2 + 1) có thể viết lại thành 1/(x + x-1). Biểu thức cải tiến này sẽ không bị tràn sớm và do số học vô hạn sẽ có giá trị chính xác khi x = 0. 1/(0 + 0-1) = 1/(0 +
Nhân mảng javascript
) = 1/
Nhân mảng javascript
= 0. Không có số học vô cực, biểu thức 1/(x + x-1) yêu cầu kiểm tra x = 0, điều này không chỉ bổ sung thêm hướng dẫn mà còn có thể làm gián đoạn quy trình. Ví dụ này minh họa một thực tế chung, cụ thể là số học vô hạn thường tránh được nhu cầu kiểm tra trường hợp đặc biệt; .

Không có chữ ký

Số 0 được biểu thị bằng số mũ emin - 1 và ý nghĩa bằng 0. Vì bit dấu có thể nhận hai giá trị khác nhau, nên có hai số không, +0 và -0. Nếu có sự khác biệt khi so sánh +0 và -0, các phép thử đơn giản như

     n = n/2
002 
     n = n/2
003 
     n = n/2
004 
     n = n/2
005 sẽ có hành vi rất khó đoán, tùy thuộc vào dấu của
     n = n/2
006. Do đó, tiêu chuẩn IEEE xác định phép so sánh sao cho +0 = -0, thay vì -0 < +0. Mặc dù luôn có thể bỏ qua dấu của số 0, nhưng tiêu chuẩn IEEE không làm như vậy. Khi phép nhân hoặc phép chia liên quan đến số 0 có dấu, quy tắc dấu thông thường được áp dụng để tính dấu của câu trả lời. Do đó 3·(+0) = +0 và +0/-3 = -0. Nếu số 0 không có dấu thì quan hệ 1/(1/x) = x sẽ không đúng khi x = ±
Nhân mảng javascript
. Lý do là 1/-
Nhân mảng javascript
và 1/+
Nhân mảng javascript
đều cho kết quả là 0 và 1/0 cho kết quả là +
Nhân mảng javascript
. Một cách để khôi phục đẳng thức 1/(1/x) = x là chỉ có một loại vô cùng, tuy nhiên điều đó sẽ dẫn đến hậu quả tai hại là làm mất dấu của một đại lượng bị tràn.

Một ví dụ khác về việc sử dụng luồng dưới mối quan tâm bằng 0 có dấu và các hàm có gián đoạn ở 0, chẳng hạn như nhật ký. Trong số học của IEEE, việc xác định log 0 = -

Nhân mảng javascript
và log x là một NaN khi x < 0 là điều đương nhiên. Giả sử rằng x đại diện cho một số âm nhỏ đã tràn đến 0. Nhờ số 0 đã ký, x sẽ âm, vì vậy nhật ký có thể trả về NaN. Tuy nhiên, nếu không có số 0 được ký, hàm nhật ký không thể phân biệt một số âm bị tràn với 0 và do đó sẽ phải trả về -
Nhân mảng javascript
. Một ví dụ khác về hàm có gián đoạn tại 0 là hàm signum, trả về dấu của một số.

Có lẽ cách sử dụng số không có dấu thú vị nhất xảy ra trong số học phức tạp. Để lấy một ví dụ đơn giản, hãy xem xét phương trình. Điều này chắc chắn đúng khi z

Nhân mảng javascript
0. Nếu z = -1, phép tính rõ ràng cho và. Như vậy,. Vấn đề có thể bắt nguồn từ thực tế là căn bậc hai có nhiều giá trị và không có cách nào để chọn các giá trị sao cho nó liên tục trong toàn bộ mặt phẳng phức. Tuy nhiên, căn bậc hai là liên tục nếu cắt nhánh bao gồm tất cả các số thực âm bị loại khỏi việc xem xét. Điều này dẫn đến vấn đề phải làm gì đối với các số thực âm, có dạng -x + i0, trong đó x > 0. Số không đã ký cung cấp một cách hoàn hảo để giải quyết vấn đề này. Các số dạng x + i(+0) có một dấu và các số dạng x + i(-0) ở phía bên kia của vết cắt nhánh có dấu khác. Trên thực tế, các công thức tính toán tự nhiên sẽ cho những kết quả này.

Quay lại. Nếu z =1 = -1 + i0 thì

1/z = 1/(-1 + i0) = [(-1- i0)]/[(-1 + i0)(-1 - i0)] = (-1 -- i0)/((-1)

và như vậy, trong khi. Do đó, số học IEEE duy trì danh tính này cho tất cả z. Một số ví dụ phức tạp hơn được đưa ra bởi Kahan [1987]. Mặc dù việc phân biệt giữa +0 và -0 có những ưu điểm nhưng đôi khi có thể gây nhầm lẫn. Ví dụ: số 0 có dấu hủy mối quan hệ x = y 

Nhân mảng javascript
 1/x = 1/y, giá trị này sai khi x = +0 và y = -0. Tuy nhiên, ủy ban IEEE đã quyết định rằng những ưu điểm của việc sử dụng dấu 0 nhiều hơn những nhược điểm.

số không chuẩn hóa

Xem xét các số dấu phẩy động chuẩn hóa với

Nhân mảng javascript
= 10, p = 3 và emin = -98. Các số x = 6. 87 × 10-97 và y = 6. 81 × 10-97 dường như là số dấu phẩy động hoàn toàn bình thường, lớn hơn hệ số 10 so với số dấu phẩy động nhỏ nhất 1. 00 × 10-98. Họ có một tài sản kỳ lạ, tuy nhiên. xy = 0 mặc dù x
Nhân mảng javascript
y. Lý do là x - y =. 06 × 10 -97  = 6. 0 × 10-99 quá nhỏ để được biểu thị dưới dạng số chuẩn hóa và do đó phải được xóa thành 0. Việc bảo quản tài sản quan trọng như thế nào

(10) x = y
Nhân mảng javascript
x - y = 0 ?

Thật dễ dàng để tưởng tượng việc viết đoạn mã,

     n = n/2
002 
     n = n/2
003 
Nhân mảng javascript
 
     n = n/2
009 
     n = n/2
010 
     n = n/2
011 
     n = n/2
004 
     n = n/2
013, và rất lâu sau đó, chương trình bị lỗi do một phép chia giả cho 0. Theo dõi các lỗi như thế này rất khó chịu và tốn thời gian. Ở mức độ triết học hơn, sách giáo khoa khoa học máy tính thường chỉ ra rằng mặc dù hiện tại việc chứng minh các chương trình lớn là đúng là không thực tế, nhưng việc thiết kế các chương trình với ý tưởng chứng minh chúng thường dẫn đến mã tốt hơn. Ví dụ: việc đưa ra các bất biến khá hữu ích, ngay cả khi chúng không được sử dụng như một phần của bằng chứng. Mã dấu phẩy động cũng giống như bất kỳ mã nào khác. nó giúp có những sự thật có thể chứng minh được để dựa vào. Ví dụ: khi phân tích công thức , sẽ rất hữu ích khi biết rằng x/2 
Nhân mảng javascript
 x y = x - y. Tương tự, biết điều đó là đúng giúp viết mã số dấu phẩy động đáng tin cậy dễ dàng hơn. Nếu nó chỉ đúng với hầu hết các số thì không thể dùng để chứng minh điều gì.

Tiêu chuẩn IEEE sử dụng các số không chuẩn hóa, đảm bảo , cũng như các mối quan hệ hữu ích khác. Chúng là phần gây tranh cãi nhất của tiêu chuẩn và có lẽ là nguyên nhân gây ra sự chậm trễ lâu dài trong việc phê duyệt 754. Hầu hết các phần cứng hiệu suất cao tuyên bố là tương thích với IEEE đều không hỗ trợ trực tiếp các số không chuẩn hóa, mà thay vào đó là các bẫy khi tiêu thụ hoặc tạo ra các số không chuẩn và để phần mềm mô phỏng tiêu chuẩn IEEE. Ý tưởng đằng sau các số không chuẩn hóa có từ Goldberg [1967] và rất đơn giản. Khi số mũ là emin, ý nghĩa và không cần phải chuẩn hóa, sao cho khi

Nhân mảng javascript
= 10, p = 3 và emin = -98, 1. 00 × 10-98 không còn là số dấu phẩy động nhỏ nhất, bởi vì 0. 98 × 10-98 cũng là một số dấu phẩy động.

Có một trở ngại nhỏ khi

Nhân mảng javascript
= 2 và một bit ẩn đang được sử dụng, vì một số có số mũ là emin sẽ luôn có nghĩa và lớn hơn hoặc bằng 1. 0 vì bit đầu ẩn. Giải pháp tương tự như giải pháp được sử dụng để biểu thị 0 và được tóm tắt trong. Số mũ emin được sử dụng để biểu diễn các biến dạng. Chính thức hơn, nếu các bit trong trường ý nghĩa là b1, b2,. , bp -1 và giá trị của số mũ là e, thì khi e > emin - 1, số được biểu thị là 1. b1b2. bp - 1 × 2e trong khi khi e = emin - 1, số được biểu diễn là 0. b1b2. bp - 1 × 2e + 1. +1 trong số mũ là cần thiết vì denormals có số mũ là emin chứ không phải emin - 1.

Nhắc lại ví dụ về

Nhân mảng javascript
= 10, p = 3, emin = -98, x = 6. 87 × 10-97 và y = 6. 81 × 10-97 trình bày ở đầu phần này. Với các giá trị không chuẩn hóa, x – y không chuyển thành 0 mà thay vào đó được biểu thị bằng số không chuẩn hóa. 6 × 10-98. Hành vi này được gọi là tràn dần. Thật dễ dàng để xác minh rằng luôn giữ khi sử dụng dòng chảy dần dần.

Nhân mảng javascript

HÌNH D-2 Xả về 0 so với dòng chảy dần

minh họa các số không chuẩn hóa. Dòng số trên cùng trong hình hiển thị các số dấu phẩy động được chuẩn hóa. Lưu ý khoảng cách giữa 0 và số chuẩn hóa nhỏ nhất. Nếu kết quả của một phép tính dấu phẩy động rơi vào khoảng trống này, nó sẽ bị xóa thành 0. Dòng số dưới cùng cho biết điều gì sẽ xảy ra khi các bất thường được thêm vào tập hợp các số dấu phẩy động. "Gulf" được điền vào và khi kết quả của phép tính nhỏ hơn, nó được biểu thị bằng bất thường gần nhất. Khi các số không chuẩn hóa được thêm vào dòng số, khoảng cách giữa các số dấu phẩy động liền kề sẽ thay đổi theo cách thông thường. các khoảng cách liền kề có cùng độ dài hoặc khác nhau theo hệ số

Nhân mảng javascript
. Nếu không có bất thường,
khoảng cách thay đổi đột ngột từ sang, đó là một yếu tố của, chứ không phải là sự thay đổi có trật tự theo hệ số của
Nhân mảng javascript
. Do đó, nhiều thuật toán có thể có sai số tương đối lớn đối với các số được chuẩn hóa gần với ngưỡng tràn sẽ hoạt động tốt trong phạm vi này khi sử dụng tràn dần.

Nếu không có dòng chảy tràn dần, biểu thức đơn giản x - y có thể có sai số tương đối rất lớn đối với các đầu vào được chuẩn hóa, như đã thấy ở trên đối với x = 6. 87 × 10-97 và y = 6. 81 × 10-97. Các lỗi tương đối lớn có thể xảy ra ngay cả khi không bị hủy bỏ, như ví dụ sau cho thấy [Demmel 1984]. Xét phép chia hai số phức a + ib và c + id. Công thức hiển nhiên

Nhân mảng javascript
· tôi

gặp vấn đề là nếu một trong hai thành phần của mẫu số c + id lớn hơn, thì công thức sẽ bị tràn, mặc dù kết quả cuối cùng có thể nằm trong phạm vi. Một phương pháp tốt hơn để tính toán các thương số là sử dụng công thức của Smith

(11)
Nhân mảng javascript

Áp dụng công thức Smith cho (2 · 10-98 + i10-98)/(4 · 10-98 + i(2 · 10-98)) cho . 5 với dòng chảy từ từ. Nó mang lại 0. 4 với flush to zero, lỗi 100 ulps. Nó là điển hình cho các số không chuẩn hóa để đảm bảo giới hạn lỗi cho các đối số xuống còn 1. 0 x.

Trình xử lý ngoại lệ, cờ và bẫy

Khi một điều kiện đặc biệt như chia cho 0 hoặc tràn xảy ra trong số học IEEE, mặc định là cung cấp kết quả và tiếp tục. Điển hình của các kết quả mặc định là NaN cho 0/0 và

Nhân mảng javascript
cho 1/0 và tràn. Các phần trước đã đưa ra các ví dụ trong đó tiến hành từ một ngoại lệ với các giá trị mặc định này là điều hợp lý để làm. Khi có bất kỳ ngoại lệ nào xảy ra, cờ trạng thái cũng được đặt. Cần triển khai tiêu chuẩn IEEE để cung cấp cho người dùng cách đọc và ghi các cờ trạng thái. Các cờ "dính" trong đó một khi được đặt, chúng vẫn được đặt cho đến khi bị xóa rõ ràng. Kiểm tra các cờ là cách duy nhất để phân biệt 1/0, đó là vô hạn chính hãng từ tràn.

Đôi khi việc tiếp tục thực thi khi đối mặt với các điều kiện ngoại lệ là không phù hợp. Phần đã đưa ra ví dụ về x/(x2 + 1). Khi x > thì mẫu số bằng vô cùng nên đáp số cuối cùng bằng 0, sai hoàn toàn. Mặc dù đối với công thức này, vấn đề có thể được giải quyết bằng cách viết lại thành 1/(x + x-1), nhưng việc viết lại có thể không phải lúc nào cũng giải quyết được vấn đề. Tiêu chuẩn IEEE khuyến nghị mạnh mẽ rằng việc triển khai cho phép cài đặt trình xử lý bẫy. Sau đó, khi một ngoại lệ xảy ra, trình xử lý bẫy được gọi thay vì đặt cờ. Giá trị được trả về bởi trình xử lý bẫy sẽ được sử dụng làm kết quả của thao tác. Trình xử lý bẫy có trách nhiệm xóa hoặc đặt cờ trạng thái;

Tiêu chuẩn IEEE chia ngoại lệ thành 5 lớp. tràn, tràn, chia cho 0, hoạt động không hợp lệ và không chính xác. Có một cờ trạng thái riêng cho từng loại ngoại lệ. Ý nghĩa của ba trường hợp ngoại lệ đầu tiên là hiển nhiên. Hoạt động không hợp lệ bao gồm các tình huống được liệt kê trong và bất kỳ so sánh nào liên quan đến NaN. Kết quả mặc định của thao tác gây ra ngoại lệ không hợp lệ là trả về NaN, nhưng điều ngược lại là không đúng. Khi một trong các toán hạng của thao tác là NaN, kết quả là NaN nhưng không có ngoại lệ không hợp lệ nào được đưa ra trừ khi thao tác cũng đáp ứng một trong các điều kiện trong

BẢNG D-4   Các ngoại lệ trong IEEE 754*Ngoại lệKết quả khi bẫy bị vô hiệuĐối số để bẫy luồng xử lý tràn±
Nhân mảng javascript
hoặc ±xmaxround(x2-
Nhân mảng javascript
)underflow0,hoặc
Nhân mảng javascript
)divide by zero±
Nhân mảng javascript
operandsinvalidNaNoperandsinexactround(x)round(x)

*x là kết quả chính xác của thao tác,

Nhân mảng javascript
= 192 cho độ chính xác đơn, 1536 cho độ chính xác kép và xmax = 1. 11. 11 ×.

Ngoại lệ không chính xác được đưa ra khi kết quả của phép toán dấu phẩy động không chính xác. Trong hệ thống

Nhân mảng javascript
= 10, p = 3, 3. 5
Nhân mảng javascript
4. 2 = 14. 7 là chính xác, nhưng 3. 5 
Nhân mảng javascript
 4. 3 = 15. 0 không chính xác (vì 3. 5 · 4. 3 = 15. 05) và đưa ra một ngoại lệ không chính xác. thảo luận về một thuật toán sử dụng ngoại lệ không chính xác. Một bản tóm tắt về hành vi của tất cả năm ngoại lệ được đưa ra trong.

Có một vấn đề triển khai liên quan đến thực tế là ngoại lệ không chính xác được đưa ra quá thường xuyên. Nếu phần cứng dấu phẩy động không có cờ của riêng nó mà thay vào đó ngắt hệ điều hành để báo hiệu một ngoại lệ dấu phẩy động, chi phí cho các ngoại lệ không chính xác có thể bị cấm. Có thể tránh được chi phí này bằng cách duy trì các cờ trạng thái bằng phần mềm. Lần đầu tiên một ngoại lệ được đưa ra, hãy đặt cờ phần mềm cho lớp thích hợp và yêu cầu phần cứng dấu phẩy động che dấu lớp ngoại lệ đó. Sau đó, tất cả các ngoại lệ tiếp theo sẽ chạy mà không làm gián đoạn hệ điều hành. Khi người dùng đặt lại cờ trạng thái đó, mặt nạ phần cứng sẽ được bật lại

Người xử lý bẫy

Một cách sử dụng rõ ràng cho trình xử lý bẫy là để tương thích ngược. Các mã cũ dự kiến ​​​​sẽ bị hủy bỏ khi xảy ra ngoại lệ có thể cài đặt trình xử lý bẫy để hủy bỏ quy trình. Điều này đặc biệt hữu ích đối với các mã có vòng lặp như

     n = n/2
014 
     n = n/2
015 
     n = n/2
016 
     n = n/2
003
     n = n/2
018
     n = n/2
019. Vì so sánh NaN với một số có <,
, >, hoặc = (chứ không phải ) luôn trả về false .
Nhân mảng javascript
, >,
Nhân mảng javascript
, or = (but not
Nhân mảng javascript
) always returns false, this code will go into an infinite loop if
     n = n/2
006 ever becomes a NaN.

Có một cách sử dụng thú vị hơn cho các trình xử lý bẫy xuất hiện khi tính toán các sản phẩm chẳng hạn như có khả năng bị tràn. Một giải pháp là sử dụng logarit và tính toán expinstead. Vấn đề với cách tiếp cận này là nó kém chính xác hơn và tốn kém hơn biểu thức đơn giản, ngay cả khi không có tràn. Có một giải pháp khác sử dụng bộ xử lý bẫy được gọi là đếm tràn/thừa để tránh cả hai vấn đề này [Sterbenz 1974]

Ý tưởng là như sau. Có một bộ đếm toàn cầu được khởi tạo bằng 0. Bất cứ khi nào tràn sản phẩm một phần cho một số k, trình xử lý bẫy sẽ tăng bộ đếm lên một và trả về số lượng bị tràn với số mũ bao quanh. Trong độ chính xác đơn của IEEE 754, emax = 127, vì vậy nếu pk = 1. 45 × 2130, nó sẽ tràn và khiến trình xử lý bẫy được gọi, trình xử lý này sẽ đưa số mũ trở lại phạm vi, thay đổi pk thành 1. 45 × 2-62 (xem bên dưới). Tương tự, nếu pk chảy tràn, bộ đếm sẽ bị giảm đi và số mũ âm sẽ được quấn quanh thành một số dương. Khi tất cả các phép nhân được thực hiện, nếu bộ đếm bằng 0 thì tích cuối cùng là pn. Nếu bộ đếm dương, sản phẩm bị tràn, nếu bộ đếm âm, sản phẩm bị tràn. Nếu không có sản phẩm nào nằm ngoài phạm vi, trình xử lý bẫy sẽ không bao giờ được gọi và việc tính toán không phát sinh thêm chi phí. Ngay cả khi có tràn/thừa, phép tính vẫn chính xác hơn nếu nó được tính bằng logarit, bởi vì mỗi pk được tính từ pk - 1 bằng cách sử dụng phép nhân chính xác đầy đủ. Barnett [1987] thảo luận về một công thức trong đó độ chính xác đầy đủ của việc đếm thừa/thừa đã gây ra lỗi trong các bảng trước đó của công thức đó

IEEE 754 chỉ định rằng khi một trình xử lý bẫy tràn hoặc bẫy tràn được gọi, nó sẽ được chuyển kết quả bao quanh làm đối số. Định nghĩa về bao quanh đối với tràn là kết quả được tính như thể với độ chính xác vô hạn, sau đó chia cho 2

Nhân mảng javascript
, rồi làm tròn thành độ chính xác phù hợp. Đối với tràn, kết quả được nhân với 2
Nhân mảng javascript
. Số mũ
Nhân mảng javascript
là 192 cho độ chính xác đơn và 1536 cho độ chính xác kép. Đây là lý do tại sao 1. 45 x 2130 đã được chuyển đổi thành 1. 45 × 2-62 trong ví dụ trên.

Chế độ làm tròn

Trong tiêu chuẩn IEEE, làm tròn số xảy ra bất cứ khi nào một thao tác có kết quả không chính xác, vì (ngoại trừ chuyển đổi thập phân nhị phân) mỗi thao tác được tính toán chính xác và sau đó được làm tròn. Theo mặc định, làm tròn có nghĩa là làm tròn về phía gần nhất. Tiêu chuẩn yêu cầu cung cấp ba chế độ làm tròn khác, đó là làm tròn về 0, làm tròn về phía +

Nhân mảng javascript
và làm tròn về phía -
Nhân mảng javascript
. Khi được sử dụng với thao tác chuyển đổi thành số nguyên, hãy làm tròn về phía -
Nhân mảng javascript
để chuyển đổi trở thành hàm sàn, trong khi làm tròn về phía +
Nhân mảng javascript
là trần. Chế độ làm tròn ảnh hưởng đến tràn, vì khi làm tròn về 0 hoặc làm tròn về -
Nhân mảng javascript
có hiệu lực, tràn độ lớn dương khiến kết quả mặc định là số lớn nhất có thể biểu thị, không phải +
Nhân mảng javascript
. Similarly, overflows of negative magnitude will produce the largest negative number when round toward +
Nhân mảng javascript
hoặc làm tròn về 0 có hiệu lực.

Một ứng dụng của chế độ làm tròn xảy ra trong số học khoảng (một ứng dụng khác được đề cập trong ). Khi sử dụng phép tính khoảng, tổng của hai số x và y là một khoảng, trong đó x

Nhân mảng javascript
y được làm tròn về phía -
Nhân mảng javascript
, và x
Nhân mảng javascript
y rounded toward +
Nhân mảng javascript
. The exact result of the addition is contained within the interval. Without rounding modes, interval arithmetic is usually implemented by computingand, whereis machine epsilon. This results in overestimates for the size of the intervals. Since the result of an operation in interval arithmetic is an interval, in general the input to an operation will also be an interval. If two intervals, and, are added, the result is, whereiswith the rounding mode set to round toward -
Nhân mảng javascript
, và với chế độ làm tròn được đặt thành làm tròn về phía +
Nhân mảng javascript
.

Khi phép tính dấu phẩy động được thực hiện bằng cách sử dụng số học khoảng, câu trả lời cuối cùng là một khoảng chứa kết quả chính xác của phép tính. Điều này không hữu ích lắm nếu khoảng thời gian hóa ra lớn (như thường xảy ra), vì câu trả lời đúng có thể nằm ở bất kỳ đâu trong khoảng thời gian đó. Số học khoảng thời gian có ý nghĩa hơn khi được sử dụng cùng với gói nhiều dấu phẩy động chính xác. Việc tính toán được thực hiện đầu tiên với một số độ chính xác p. Nếu số học khoảng cho thấy rằng câu trả lời cuối cùng có thể không chính xác, phép tính được thực hiện lại với độ chính xác cao hơn và cao hơn cho đến khi khoảng cuối cùng có kích thước hợp lý

cờ

Tiêu chuẩn IEEE có một số cờ và chế độ. Như đã thảo luận ở trên, có một cờ trạng thái cho mỗi trong số năm trường hợp ngoại lệ. tràn, tràn, chia cho 0, hoạt động không hợp lệ và không chính xác. Có bốn chế độ làm tròn. làm tròn về phía gần nhất, làm tròn về phía +

Nhân mảng javascript
, làm tròn về 0 và làm tròn về -
Nhân mảng javascript
. Chúng tôi đặc biệt khuyến nghị rằng có một bit chế độ kích hoạt cho mỗi trong số năm trường hợp ngoại lệ. Phần này đưa ra một số ví dụ đơn giản về cách sử dụng tốt các chế độ và cờ này. Một ví dụ phức tạp hơn được thảo luận trong phần.

Xem xét việc viết một chương trình con để tính xn, trong đó n là một số nguyên. Khi n > 0, một quy trình đơn giản như

     n = n/2
9
     n = n/2
0
     n = n/2
1
     n = n/2
     n = n/2
3
     n = n/2
4
     n = n/2
5
     n = n/2
     if (n==0) return u
     n = n/2
1
     n = n/2
00
     n = n/2
3

Nếu n < 0, thì cách tính xn chính xác hơn không phải là gọi

     n = n/2
021
     n = n/2
022 mà là gọi
     n = n/2
023
     n = n/2
022, bởi vì biểu thức đầu tiên nhân n đại lượng, mỗi đại lượng có sai số làm tròn từ phép chia (i. e. , 1/x). Trong biểu thức thứ hai đây là chính xác (i. e. , x) và phép chia cuối cùng chỉ mắc thêm một lỗi làm tròn. Thật không may, đây là một trở ngại nhỏ trong chiến lược này. Nếu
     n = n/2
025
     n = n/2
022 tràn, thì trình xử lý bẫy tràn sẽ được gọi hoặc nếu không thì cờ trạng thái tràn sẽ được đặt. Điều này là không chính xác, bởi vì nếu x-n tràn dưới thì xn sẽ tràn hoặc nằm trong phạm vi. Nhưng vì tiêu chuẩn IEEE cho phép người dùng truy cập vào tất cả các cờ, nên chương trình con có thể dễ dàng sửa lỗi này. Nó chỉ đơn giản là tắt các bit kích hoạt bẫy tràn và bẫy tràn và lưu các bit trạng thái tràn và tràn. Sau đó, nó tính toán
     n = n/2
023
     n = n/2
022. Nếu cả bit trạng thái tràn và tràn không được đặt, nó sẽ khôi phục chúng cùng với các bit kích hoạt bẫy. Nếu một trong các bit trạng thái được đặt, nó sẽ khôi phục các cờ và thực hiện lại phép tính bằng cách sử dụng
     n = n/2
021
     n = n/2
022, điều này khiến các ngoại lệ chính xác xảy ra

Một ví dụ khác về việc sử dụng cờ xảy ra khi tính toán arccos thông qua công thức

arccos x = 2 arctan
Nhân mảng javascript
.

Nếu arctan(

Nhân mảng javascript
) ước tính thành
Nhân mảng javascript
/2, thì arccos(-1) sẽ ước tính chính xác thành 2·arctan(
Nhân mảng javascript
) =
Nhân mảng javascript
, because of infinity arithmetic. However, there is a small snag, because the computation of (1 - x)/(1 + x) will cause the divide by zero exception flag to be set, even though arccos(-1) is not exceptional. The solution to this problem is straightforward. Simply save the value of the divide by zero flag before computing arccos, and then restore its old value after the computation.

khía cạnh hệ thống

Việc thiết kế hầu hết mọi khía cạnh của hệ thống máy tính đòi hỏi kiến ​​thức về dấu phẩy động. Kiến trúc máy tính thường có các lệnh dấu phẩy động, trình biên dịch phải tạo các lệnh dấu phẩy động đó và hệ điều hành phải quyết định phải làm gì khi các điều kiện ngoại lệ được đưa ra cho các lệnh dấu phẩy động đó. Các nhà thiết kế hệ thống máy tính hiếm khi nhận được hướng dẫn từ các văn bản phân tích số, thường hướng đến người dùng và người viết phần mềm, không phải các nhà thiết kế máy tính. Để làm ví dụ về cách các quyết định thiết kế hợp lý có thể dẫn đến hành vi không mong muốn, hãy xem xét chương trình BASIC sau

     n = n/2
02
     n = n/2
03
     n = n/2
04

Khi được biên dịch và chạy bằng Borland's Turbo Basic trên PC IBM, chương trình sẽ in

     n = n/2
031
     n = n/2
032. Ví dụ này sẽ được phân tích trong phần tiếp theo

Ngẫu nhiên, một số người nghĩ rằng giải pháp cho những bất thường như vậy là không bao giờ so sánh các số dấu phẩy động bằng nhau, mà thay vào đó coi chúng bằng nhau nếu chúng nằm trong giới hạn lỗi nào đó E. Đây khó có thể là thuốc chữa bách bệnh vì nó đặt ra nhiều câu hỏi như câu trả lời. Giá trị của E phải là bao nhiêu? . một - b. < E, không phải là quan hệ tương đương vì a ~ b và b ~ c không ngụ ý rằng a ~ c.

Nhân mảng javascript
|a - b| < E, is not an equivalence relation because a ~ b and b ~ c does not imply that a ~ c.

Bộ hướng dẫn

Thuật toán yêu cầu một đợt ngắn có độ chính xác cao hơn để tạo ra kết quả chính xác là điều khá phổ biến. Một ví dụ xảy ra trong công thức bậc hai ()/2a. Như đã thảo luận trong phần , khi b2

Nhân mảng javascript
4ac, lỗi làm tròn có thể làm ô nhiễm đến một nửa chữ số trong các căn được tính theo công thức bậc hai. Bằng cách thực hiện phép tính con của b2 - 4ac với độ chính xác kép, một nửa số bit có độ chính xác kép của gốc bị mất, điều đó có nghĩa là tất cả các bit có độ chính xác đơn được giữ nguyên.

Việc tính toán b2 - 4ac với độ chính xác kép khi mỗi đại lượng a, b và c đều ở độ chính xác đơn sẽ dễ dàng nếu có một lệnh nhân lấy hai số có độ chính xác đơn và tạo ra kết quả chính xác kép. Để tạo ra tích được làm tròn chính xác của hai số có p chữ số, một bộ nhân cần tạo ra toàn bộ 2p bit của tích, mặc dù nó có thể loại bỏ các bit khi nó tiếp tục. Do đó, phần cứng để tính toán một tích có độ chính xác kép từ các toán hạng có độ chính xác đơn thường sẽ chỉ đắt hơn một chút so với hệ số nhân chính xác đơn và rẻ hơn nhiều so với hệ số nhân chính xác kép. Mặc dù vậy, các tập lệnh hiện đại có xu hướng chỉ cung cấp các lệnh tạo ra kết quả có độ chính xác tương tự như các toán hạng

Nếu một lệnh kết hợp hai toán hạng chính xác đơn lẻ để tạo ra một sản phẩm có độ chính xác kép chỉ hữu ích cho công thức bậc hai, thì nó sẽ không đáng để thêm vào một tập lệnh. Tuy nhiên, hướng dẫn này có nhiều công dụng khác. Xét bài toán giải hệ phương trình tuyến tính,

a11x1 + a12x2 + · · · + a1nxn= b1
a21x1 + a22x2 + · · · + a2nxn= b2
· · ·an1x1 + an2x2 + · · ·+ annxn= bn

có thể được viết dưới dạng ma trận là Ax = b, trong đó

Nhân mảng javascript

Giả sử rằng một giải pháp x (1) được tính toán bằng một số phương pháp, có lẽ là loại bỏ Gaussian. Có một cách đơn giản để cải thiện độ chính xác của kết quả được gọi là cải tiến lặp đi lặp lại. tính toán đầu tiên

(12)
Nhân mảng javascript
= Ax(1) - b

rồi giải hệ

(13) Ay =
Nhân mảng javascript

Lưu ý rằng nếu x(1) là nghiệm chính xác thì

Nhân mảng javascript
là vectơ 0, y cũng vậy. Nói chung, việc tính toán
Nhân mảng javascript
và y sẽ phát sinh lỗi làm tròn, vì vậy Ay 
Nhân mảng javascript
 
Nhân mảng javascript
 
Nhân mảng javascript
 Ax(1) - b = A(x(1) - x), where x is the (unknown) true solution. Then y 
Nhân mảng javascript
 x(1) - x, do đó, ước tính cải tiến cho giải pháp là

(14) x(2) = x(1) - y

Ba bước , , và có thể được lặp lại, thay x(1) bằng x(2) và x(2) bằng x(3). Lập luận rằng x(i + 1) chính xác hơn x(i) chỉ là không chính thức. Để biết thêm thông tin, xem [Golub và Vân Loan 1989]

Khi thực hiện cải tiến lặp lại,

Nhân mảng javascript
là một vectơ có các phần tử là hiệu của các số dấu phẩy động không chính xác gần đó và do đó có thể bị hủy bỏ nghiêm trọng. Do đó, cải tiến lặp đi lặp lại không hữu ích lắm trừ khi
Nhân mảng javascript
= Ax(1) - b được tính toán với độ chính xác kép. Một lần nữa, đây là trường hợp tính toán tích của hai số chính xác đơn (A và x(1)), trong đó cần có kết quả chính xác kép đầy đủ.

Tóm lại, các lệnh nhân hai số dấu phẩy động và trả về một tích có độ chính xác gấp đôi toán hạng tạo nên một bổ sung hữu ích cho tập lệnh dấu phẩy động. Một số ý nghĩa của điều này đối với trình biên dịch sẽ được thảo luận trong phần tiếp theo

Ngôn ngữ và Trình biên dịch

Sự tương tác của trình biên dịch và dấu phẩy động được thảo luận trong Farnum [1988], và phần lớn nội dung thảo luận trong phần này được lấy từ bài báo đó

mơ hồ

Lý tưởng nhất là một định nghĩa ngôn ngữ nên xác định ngữ nghĩa của ngôn ngữ đủ chính xác để chứng minh các tuyên bố về chương trình. Mặc dù điều này thường đúng với phần nguyên của ngôn ngữ, nhưng các định nghĩa ngôn ngữ thường có vùng màu xám lớn khi nói đến dấu phẩy động. Có lẽ điều này là do thực tế là nhiều nhà thiết kế ngôn ngữ tin rằng không thể chứng minh được gì về dấu phẩy động, vì nó kéo theo lỗi làm tròn. Nếu vậy, các phần trước đã chứng minh sự ngụy biện trong lập luận này. Phần này thảo luận về một số vùng màu xám phổ biến trong các định nghĩa ngôn ngữ, bao gồm các đề xuất về cách giải quyết chúng

Đáng chú ý là, một số ngôn ngữ không xác định rõ ràng rằng nếu

     n = n/2
006 là một biến dấu phẩy động (có giá trị là
     n = n/2
034), thì mọi lần xuất hiện của (giả sử)
     n = n/2
035 phải có cùng giá trị. Ví dụ Ada, dựa trên mô hình của Brown, dường như ngụ ý rằng số học dấu phẩy động chỉ phải thỏa mãn các tiên đề của Brown, và do đó các biểu thức có thể có một trong nhiều giá trị có thể. Suy nghĩ về dấu phẩy động theo cách mờ nhạt này trái ngược hoàn toàn với mô hình IEEE, trong đó kết quả của mỗi phép toán dấu phẩy động được xác định chính xác. Trong mô hình IEEE, chúng ta có thể chứng minh rằng
     n = n/2
036 ước lượng thành
     n = n/2
037 (Định lý 7). Trong mô hình của Brown, chúng ta không thể

Một sự mơ hồ khác trong hầu hết các định nghĩa ngôn ngữ liên quan đến những gì xảy ra khi tràn, tràn và các ngoại lệ khác. Tiêu chuẩn IEEE chỉ định chính xác hành vi của các ngoại lệ và do đó, các ngôn ngữ sử dụng tiêu chuẩn làm mẫu có thể tránh mọi sự mơ hồ về điểm này

Một khu vực màu xám khác liên quan đến việc giải thích các dấu ngoặc đơn. Do lỗi làm tròn, các định luật kết hợp của đại số không nhất thiết đúng cho các số dấu phẩy động. Ví dụ: biểu thức

     n = n/2
038 có đáp án hoàn toàn khác với
     n = n/2
039 khi x = 1030, y = -1030 và z = 1 (trường hợp trước là 1, trường hợp sau là 0). Tầm quan trọng của việc bảo toàn dấu ngoặc đơn không thể được nhấn mạnh quá mức. Các thuật toán trình bày trong định lý 3, 4 và 6 đều phụ thuộc vào nó. Ví dụ, trong Định lý 6, công thức xh = mx - (mx - x) sẽ rút gọn thành xh = x nếu không có dấu ngoặc đơn, do đó phá hủy toàn bộ thuật toán. Một định nghĩa ngôn ngữ không yêu cầu dấu ngoặc đơn được vinh danh là vô ích đối với các phép tính dấu phẩy động

Đánh giá biểu thức con được xác định không chính xác trong nhiều ngôn ngữ. Giả sử rằng

     n = n/2
040 là độ chính xác kép, nhưng
     n = n/2
006 và
     n = n/2
042 là độ chính xác đơn. Vậy thì trong biểu thức
     n = n/2
040 
     n = n/2
044 
     n = n/2
045 sản phẩm được thực hiện ở độ chính xác đơn hay kép? . trong
     n = n/2
006
     n = n/2
044
     n = n/2
048 trong đó
     n = n/2
049 và
     n = n/2
050 là số nguyên, phép chia là phép toán số nguyên hay phép toán dấu phẩy động? . Đầu tiên là yêu cầu tất cả các biến trong một biểu thức có cùng kiểu. Đây là giải pháp đơn giản nhất, nhưng có một số nhược điểm. Trước hết, các ngôn ngữ như Pascal có các kiểu dải con cho phép trộn các biến dải con với các biến số nguyên, do đó, việc cấm trộn các biến chính xác đơn và kép là hơi kỳ lạ. Một vấn đề khác liên quan đến hằng số. Trong biểu thức
     n = n/2
051, hầu hết các ngôn ngữ diễn giải 0. 1 là một hằng số chính xác duy nhất. Bây giờ, giả sử lập trình viên quyết định thay đổi khai báo của tất cả các biến dấu phẩy động từ độ chính xác đơn thành độ chính xác kép. Nếu 0. 1 vẫn được coi là một hằng số chính xác duy nhất, thì sẽ có lỗi thời gian biên dịch. Lập trình viên sẽ phải tìm kiếm và thay đổi mọi hằng số dấu phẩy động

Cách tiếp cận thứ hai là cho phép các biểu thức hỗn hợp, trong trường hợp đó phải cung cấp các quy tắc để đánh giá biểu thức con. Có một số ví dụ hướng dẫn. Định nghĩa ban đầu của C yêu cầu mọi biểu thức dấu phẩy động được tính toán với độ chính xác kép [Kernighan và Ritchie 1978]. Điều này dẫn đến sự bất thường như ví dụ ở đầu phần này. Biểu thức

     n = n/2
052 được tính toán với độ chính xác kép, nhưng nếu
     n = n/2
053 là một biến có độ chính xác đơn, thì thương số được làm tròn thành độ chính xác đơn để lưu trữ. Vì 3/7 là một phân số nhị phân lặp lại, nên giá trị được tính toán của nó ở độ chính xác kép khác với giá trị được lưu trữ ở độ chính xác đơn. Do đó phép so sánh q = 3/7 không thành công. Điều này cho thấy rằng tính toán mọi biểu thức ở độ chính xác cao nhất hiện có không phải là một quy tắc tốt

Một ví dụ hướng dẫn khác là các sản phẩm bên trong. Nếu tích bên trong có hàng nghìn số hạng, lỗi làm tròn trong tổng có thể trở nên đáng kể. Một cách để giảm lỗi làm tròn này là cộng dồn các tổng với độ chính xác kép (điều này sẽ được thảo luận chi tiết hơn trong phần ). Nếu

     n = n/2
054 là một biến có độ chính xác kép và
     n = n/2
055 và
     n = n/2
056 là các mảng có độ chính xác đơn, thì vòng lặp tích bên trong sẽ giống như
     n = n/2
054 
     n = n/2
004
     n = n/2
054
     n = n/2
044
     n = n/2
061. Nếu phép nhân được thực hiện với độ chính xác đơn, thì phần lớn lợi thế của tích lũy độ chính xác kép sẽ bị mất đi, bởi vì sản phẩm bị cắt bớt thành độ chính xác đơn ngay trước khi được thêm vào biến chính xác kép

Một quy tắc áp dụng cho cả hai ví dụ trước đó là tính toán một biểu thức với độ chính xác cao nhất của bất kỳ biến nào xuất hiện trong biểu thức đó. Sau đó,

     n = n/2
053 
     n = n/2
004 
     n = n/2
052 sẽ được tính toán hoàn toàn với độ chính xác đơn và sẽ có giá trị boolean là true, trong khi đó,
     n = n/2
054
     n = n/2
004
     n = n/2
054
     n = n/2
044
     n = n/2
061 sẽ được tính toán với độ chính xác kép, đạt được toàn bộ lợi thế của tích lũy độ chính xác kép. Tuy nhiên, quy tắc này quá đơn giản để bao gồm tất cả các trường hợp một cách rõ ràng. Nếu
     n = n/2
070 và
     n = n/2
071 là các biến có độ chính xác kép, thì biểu thức
     n = n/2
042 
     n = n/2
004 
     n = n/2
006 
     n = n/2
044 
     n = n/2
076 chứa một biến có độ chính xác kép, nhưng việc tính tổng theo độ chính xác kép sẽ là vô nghĩa, bởi vì cả hai toán hạng đều có độ chính xác đơn, kết quả cũng vậy

Một quy tắc đánh giá biểu thức con phức tạp hơn như sau. Trước tiên, gán cho mỗi thao tác một độ chính xác dự kiến, là độ chính xác tối đa của các toán hạng của nó. Phép gán này phải được thực hiện từ lá đến gốc của cây biểu thức. Sau đó thực hiện chuyền thứ hai từ gốc đến lá. Trong bước này, hãy chỉ định cho mỗi thao tác độ chính xác dự kiến ​​tối đa và độ chính xác mà cấp độ gốc mong đợi. Trong trường hợp của

     n = n/2
053 
     n = n/2
004 
     n = n/2
052, mỗi lá là độ chính xác duy nhất, vì vậy tất cả các thao tác được thực hiện với độ chính xác duy nhất. Trong trường hợp của
     n = n/2
054 
     n = n/2
004 
     n = n/2
054 
     n = n/2
044 
     n = n/2
061, độ chính xác dự kiến ​​của phép toán nhân là độ chính xác đơn, nhưng ở lần thứ hai, phép toán này được tăng lên độ chính xác kép vì phép toán mẹ của nó yêu cầu toán hạng có độ chính xác kép. Và trong
     n = n/2
042 
     n = n/2
004 
     n = n/2
006 
     n = n/2
044 
     n = n/2
076, việc bổ sung được thực hiện với độ chính xác duy nhất. Farnum [1988] trình bày bằng chứng rằng thuật toán này không khó thực hiện

Nhược điểm của quy tắc này là việc đánh giá một biểu thức con phụ thuộc vào biểu thức mà nó được nhúng vào. Điều này có thể có một số hậu quả khó chịu. Ví dụ: giả sử bạn đang gỡ lỗi chương trình và muốn biết giá trị của biểu thức con. Bạn không thể chỉ cần nhập biểu thức con vào trình gỡ lỗi và yêu cầu đánh giá nó, bởi vì giá trị của biểu thức con trong chương trình phụ thuộc vào biểu thức mà nó được nhúng vào. Nhận xét cuối cùng về biểu thức con. vì việc chuyển đổi hằng số thập phân thành nhị phân là một phép toán nên quy tắc đánh giá cũng ảnh hưởng đến việc giải thích hằng số thập phân. Điều này đặc biệt quan trọng đối với các hằng số như

     n = n/2
090 không thể biểu diễn chính xác ở dạng nhị phân

Một vùng màu xám tiềm năng khác xảy ra khi một ngôn ngữ bao gồm phép lũy thừa như một trong những hoạt động tích hợp của ngôn ngữ đó. Không giống như các phép tính số học cơ bản, giá trị của phép lũy thừa không phải lúc nào cũng rõ ràng [Kahan và Coonen 1982]. Nếu

     n = n/2
091 là toán tử lũy thừa, thì chắc chắn
     n = n/2
092 có giá trị -27. Tuy nhiên,
     n = n/2
093 có vấn đề. Nếu toán tử ________ 3091 kiểm tra các lũy thừa nguyên, nó sẽ tính ________ 3093 là -3. 03 = -27. Mặt khác, nếu công thức xy = eylogx được sử dụng để xác định
     n = n/2
091 cho các đối số thực, thì tùy thuộc vào hàm nhật ký, kết quả có thể là NaN (sử dụng định nghĩa tự nhiên của log(x) =
     n = n/2
097 khi x < 0). Tuy nhiên, nếu hàm FORTRAN
     n = n/2
098 được sử dụng thì câu trả lời sẽ là -27, vì tiêu chuẩn ANSI FORTRAN định nghĩa
     n = n/2
099 là i
Nhân mảng javascript
+ log 3 [ANSI 1978]. Ngôn ngữ lập trình Ada tránh vấn đề này bằng cách chỉ xác định lũy thừa cho lũy thừa nguyên, trong khi ANSI FORTRAN cấm nâng số âm lên lũy thừa thực.

Trên thực tế, tiêu chuẩn FORTRAN nói rằng

Bất kỳ phép tính số học nào có kết quả không được xác định bằng toán học đều bị cấm

Thật không may, với sự ra đời của ±

Nhân mảng javascript
theo tiêu chuẩn IEEE, ý nghĩa của từ không được xác định bằng toán học không còn hoàn toàn rõ ràng nữa. Một định nghĩa có thể là sử dụng phương pháp được trình bày trong phần. Ví dụ: để xác định giá trị của ab, hãy xem xét các hàm giải tích không hằng số f và g với tính chất f(x)
Nhân mảng javascript
a và g(x)
Nhân mảng javascript
b as x
Nhân mảng javascript
0. If f(x)g(x) always approaches the same limit, then this should be the value of ab. This definition would set 2
Nhân mảng javascript
 = 
Nhân mảng javascript
có vẻ khá hợp lý. trong trường hợp 1. 0
Nhân mảng javascript
, khi f(x) = 1 và g(x) = 1/x thì giới hạn tiến tới 1, nhưng khi f(x) = 1 - x và g(x) = 1/ . Vậy 1. 0
Nhân mảng javascript
, phải là NaN. Trong trường hợp 00, f(x)g(x) = eg(x)log f(x). Vì f và g là giải tích và nhận giá trị 0 tại 0 nên f(x) = a1x1 + a2x2 +. và g(x) = b1x1 + b2x2 +. Do đó limx
Nhân mảng javascript
0g(x) log f(x) = limx 
Nhân mảng javascript
 0x log(x(a1 + a2x +. )) = limx
Nhân mảng javascript
0x log(a1x) = 0. Vậy f(x)g(x)
Nhân mảng javascript
e0 = 1 với mọi f và g, nghĩa là 00 = 1. Sử dụng định nghĩa này sẽ xác định rõ ràng hàm số mũ cho tất cả các đối số và đặc biệt sẽ xác định
     n = n/2
093 là -27.

Tiêu chuẩn IEEE

Phần ," đã thảo luận về nhiều tính năng của tiêu chuẩn IEEE. Tuy nhiên, tiêu chuẩn IEEE không nói gì về cách truy cập các tính năng này từ ngôn ngữ lập trình. Do đó, thường có sự không phù hợp giữa phần cứng dấu phẩy động hỗ trợ tiêu chuẩn và ngôn ngữ lập trình như C, Pascal hoặc FORTRAN. Một số khả năng của IEEE có thể được truy cập thông qua thư viện các cuộc gọi chương trình con. Ví dụ: tiêu chuẩn IEEE yêu cầu căn bậc hai phải được làm tròn chính xác và hàm căn bậc hai thường được triển khai trực tiếp trong phần cứng. Chức năng này có thể dễ dàng truy cập thông qua một thói quen căn bậc hai của thư viện. Tuy nhiên, các khía cạnh khác của tiêu chuẩn không dễ thực hiện như chương trình con. Ví dụ: hầu hết các ngôn ngữ máy tính chỉ định nhiều nhất hai loại dấu phẩy động, trong khi tiêu chuẩn IEEE có bốn độ chính xác khác nhau (mặc dù các cấu hình được đề xuất là đơn cộng với mở rộng đơn hoặc mở rộng đơn, kép và mở rộng kép). Infinity cung cấp một ví dụ khác. Các hằng để biểu diễn ±

Nhân mảng javascript
có thể được cung cấp bởi một chương trình con. Nhưng điều đó có thể khiến chúng không sử dụng được ở những nơi yêu cầu biểu thức hằng, chẳng hạn như bộ khởi tạo biến hằng.

Một tình huống tinh tế hơn là thao túng trạng thái liên quan đến tính toán, trong đó trạng thái bao gồm các chế độ làm tròn, bit kích hoạt bẫy, trình xử lý bẫy và cờ ngoại lệ. Một cách tiếp cận là cung cấp các chương trình con để đọc và ghi trạng thái. Ngoài ra, một cuộc gọi duy nhất có thể đặt nguyên tử một giá trị mới và trả về giá trị cũ thường hữu ích. Như các ví dụ trong phần này cho thấy, một mô hình sửa đổi trạng thái IEEE rất phổ biến là chỉ thay đổi nó trong phạm vi của một khối hoặc chương trình con. Do đó, lập trình viên phải tìm từng lối ra khỏi khối và đảm bảo trạng thái được khôi phục. Hỗ trợ ngôn ngữ để đặt trạng thái chính xác trong phạm vi của một khối sẽ rất hữu ích ở đây. Modula-3 là một ngôn ngữ triển khai ý tưởng này cho trình xử lý bẫy [Nelson 1991]

Có một số điểm nhỏ cần được xem xét khi triển khai tiêu chuẩn IEEE trong một ngôn ngữ. Vì x - x = +0 với mọi x nên (+0) - (+0) = +0. Tuy nhiên, -(+0) = -0, do đó -x không được định nghĩa là 0 - x. Việc giới thiệu NaN có thể gây nhầm lẫn, vì một NaN không bao giờ bằng bất kỳ số nào khác (bao gồm cả NaN khác), vì vậy x = x không còn đúng nữa. Trên thực tế, biểu thức x

Nhân mảng javascript
x là cách đơn giản nhất để kiểm tra NaN nếu chức năng đề xuất của IEEE
     n = n/2
101 không được cung cấp. Hơn nữa, NaN không có thứ tự đối với tất cả các số khác, vì vậy x
Nhân mảng javascript
y không thể được định nghĩa là không phải x > y. Do việc sử dụng NaN làm cho các số dấu phẩy động trở nên có thứ tự một phần, nên hàm
     n = n/2
102 trả về một trong các

Mặc dù tiêu chuẩn IEEE xác định các hoạt động dấu phẩy động cơ bản để trả về NaN nếu bất kỳ toán hạng nào là NaN, đây có thể không phải lúc nào cũng là định nghĩa tốt nhất cho các hoạt động phức hợp. Ví dụ: khi tính toán hệ số tỷ lệ thích hợp để sử dụng trong việc vẽ đồ thị, giá trị lớn nhất của một tập hợp giá trị phải được tính toán. Trong trường hợp này, điều hợp lý là thao tác tối đa chỉ cần bỏ qua NaN

Cuối cùng, làm tròn có thể là một vấn đề. Tiêu chuẩn IEEE xác định làm tròn rất chính xác và nó phụ thuộc vào giá trị hiện tại của các chế độ làm tròn. Điều này đôi khi xung đột với định nghĩa làm tròn ẩn trong chuyển đổi loại hoặc hàm

     n = n/2
103 rõ ràng trong các ngôn ngữ. Điều này có nghĩa là các chương trình muốn sử dụng phương pháp làm tròn IEEE không thể sử dụng các ngôn ngữ gốc của ngôn ngữ tự nhiên và ngược lại, các ngôn ngữ gốc sẽ không hiệu quả để triển khai trên số lượng máy IEEE ngày càng tăng

Trình tối ưu hóa

Văn bản trình biên dịch có xu hướng bỏ qua chủ đề về dấu phẩy động. Ví dụ Aho et al. [1986] đề cập đến việc thay thế

     n = n/2
104 bằng
     n = n/2
105, khiến người đọc cho rằng nên thay thế
     n = n/2
106 bằng
     n = n/2
051. Tuy nhiên, hai biểu thức này không có cùng ngữ nghĩa trên máy nhị phân, vì 0. 1 không thể được biểu diễn chính xác ở dạng nhị phân. Sách giáo khoa này cũng gợi ý thay thế
     n = n/2
108 bằng
     n = n/2
109, mặc dù chúng ta đã thấy rằng hai biểu thức này có thể có các giá trị hoàn toàn khác nhau khi y
Nhân mảng javascript
z. Mặc dù nó đủ điều kiện tuyên bố rằng bất kỳ nhận dạng đại số nào cũng có thể được sử dụng khi tối ưu hóa mã bằng cách lưu ý rằng các trình tối ưu hóa không được vi phạm định nghĩa ngôn ngữ, nhưng nó để lại ấn tượng rằng ngữ nghĩa dấu phẩy động không quan trọng lắm. Cho dù tiêu chuẩn ngôn ngữ có quy định rằng dấu ngoặc đơn phải được tôn trọng hay không, thì
     n = n/2
038 có thể có một câu trả lời hoàn toàn khác với
     n = n/2
039, như đã thảo luận ở trên. Có một vấn đề liên quan mật thiết đến việc bảo toàn dấu ngoặc đơn được minh họa bằng đoạn mã sau

     n = n/2
05
     n = n/2
06


:

Điều này được thiết kế để đưa ra ước tính cho máy epsilon. Nếu trình biên dịch tối ưu hóa thông báo rằng eps + 1 > 1

Nhân mảng javascript
eps > 0, chương trình sẽ bị thay đổi hoàn toàn. Thay vì tính số x nhỏ nhất sao cho 1
Nhân mảng javascript
x vẫn lớn hơn x (x
Nhân mảng javascript
e
Nhân mảng javascript
), nó sẽ . Việc tránh loại "tối ưu hóa" này quan trọng đến mức đáng để trình bày thêm một thuật toán rất hữu ích nhưng nó hoàn toàn bị nó phá hỏng.
Nhân mảng javascript
). Avoiding this kind of "optimization" is so important that it is worth presenting one more very useful algorithm that is totally ruined by it.

Nhiều bài toán, chẳng hạn như tích phân số và nghiệm số của phương trình vi phân liên quan đến việc tính tổng với nhiều số hạng. Bởi vì mỗi bổ sung có khả năng gây ra một lỗi lớn như. 5 ulp, một tổng bao gồm hàng nghìn số hạng có thể có khá nhiều lỗi làm tròn. Một cách đơn giản để khắc phục điều này là lưu trữ phần tóm tắt một phần trong biến độ chính xác kép và thực hiện từng phép cộng bằng cách sử dụng độ chính xác kép. Nếu phép tính đang được thực hiện với độ chính xác đơn, thì việc tính tổng với độ chính xác kép sẽ dễ dàng trên hầu hết các hệ thống máy tính. Tuy nhiên, nếu phép tính đã được thực hiện với độ chính xác kép, thì việc nhân đôi độ chính xác không đơn giản như vậy. Một phương pháp đôi khi được ủng hộ là sắp xếp các số và cộng chúng từ nhỏ nhất đến lớn nhất. Tuy nhiên, có một phương pháp hiệu quả hơn nhiều giúp cải thiện đáng kể độ chính xác của các khoản tiền, đó là

Định lý 8 (Công thức tổng Kahan)

Giả sử điều đó được tính toán bằng thuật toán sau
     n = n/2
07
     n = n/2
08______309
     n = n/2
10
     n = n/2
11
     n = n/2
12
     n = n/2
13
     n = n/2
14
Sau đó, tổng S được tính toán bằng với vị trí.

Sử dụng công thức ngây thơ, tổng được tính bằng.

Nhân mảng javascript
j.

Trình tối ưu hóa tin rằng số học dấu phẩy động tuân theo định luật đại số sẽ kết luận rằng C = [T-S] - Y = [(S+Y)-S] - Y = 0, khiến thuật toán hoàn toàn vô dụng. Những ví dụ này có thể được tóm tắt bằng cách nói rằng các trình tối ưu hóa phải cực kỳ thận trọng khi áp dụng các đồng nhất đại số chứa các số thực toán học cho các biểu thức liên quan đến các biến dấu phẩy động

Một cách khác mà trình tối ưu hóa có thể thay đổi ngữ nghĩa của mã dấu phẩy động liên quan đến các hằng số. Trong biểu thức

     n = n/2
112, có một phép toán chuyển đổi số thập phân thành nhị phân ngầm để chuyển đổi số thập phân thành hằng số nhị phân. Bởi vì hằng số này không thể được biểu diễn chính xác ở dạng nhị phân, ngoại lệ không chính xác sẽ được nêu ra. Ngoài ra, nên đặt cờ dòng dưới nếu biểu thức được đánh giá theo độ chính xác đơn. Vì hằng số không chính xác nên việc chuyển đổi chính xác thành nhị phân phụ thuộc vào giá trị hiện tại của các chế độ làm tròn IEEE. Do đó, một trình tối ưu hóa chuyển đổi
     n = n/2
113 thành nhị phân tại thời điểm biên dịch sẽ thay đổi ngữ nghĩa của chương trình. Tuy nhiên, các hằng số như 27. 5 có thể biểu diễn chính xác ở độ chính xác nhỏ nhất hiện có có thể được chuyển đổi một cách an toàn tại thời điểm biên dịch, vì chúng luôn chính xác, không thể đưa ra bất kỳ ngoại lệ nào và không bị ảnh hưởng bởi các chế độ làm tròn. Các hằng số dự định được chuyển đổi tại thời điểm biên dịch nên được thực hiện với một khai báo hằng số, chẳng hạn như
     n = n/2
114
     n = n/2
115
     n = n/2
004
     n = n/2
117

Loại bỏ biểu thức con phổ biến là một ví dụ khác về tối ưu hóa có thể thay đổi ngữ nghĩa dấu phẩy động, như được minh họa bằng đoạn mã sau

     n = n/2
15
     n = n/2
16____217

Mặc dù

     n = n/2
118 có vẻ là một biểu thức con phổ biến, nhưng không phải vì chế độ làm tròn khác nhau ở hai vị trí đánh giá. Ba ví dụ cuối cùng. x = x không thể được thay thế bằng hằng số boolean
     n = n/2
119, vì nó không thành công khi x là NaN; .
Nhân mảng javascript
y, because NaNs are neither greater than nor less than ordinary floating-point numbers.

Bất chấp những ví dụ này, vẫn có những tối ưu hóa hữu ích có thể được thực hiện trên mã dấu phẩy động. Trước hết, có các đẳng thức đại số hợp lệ cho các số dấu phẩy động. Một số ví dụ trong số học IEEE là x + y = y + x, 2 ×  x = x + x, 1 × x = x và 0. 5× x = x/2. Tuy nhiên, ngay cả những nhận dạng đơn giản này cũng có thể bị lỗi trên một số máy như siêu máy tính CDC và Cray. Lập lịch trình hướng dẫn và thay thế thủ tục nội tuyến là hai cách tối ưu hóa hữu ích tiềm năng khác

Như một ví dụ cuối cùng, hãy xem xét biểu thức

     n = n/2
070 
     n = n/2
004 
     n = n/2
045, trong đó
     n = n/2
006 và
     n = n/2
042 là các biến chính xác đơn và
     n = n/2
070 là biến chính xác kép. Trên các máy có lệnh nhân hai số chính xác đơn lẻ để tạo ra số chính xác kép,
     n = n/2
070 
     n = n/2
004 
     n = n/2
045 có thể được ánh xạ tới lệnh đó, thay vì được biên dịch thành một loạt lệnh chuyển đổi toán hạng thành gấp đôi rồi thực hiện nhân đôi thành gấp đôi

Một số tác giả trình biên dịch xem các hạn chế cấm chuyển đổi (x + y) + z thành x + (y + z) là không liên quan, chỉ quan tâm đến các lập trình viên sử dụng các thủ thuật không thể chuyển đổi. Có lẽ họ đã nghĩ rằng các số dấu phẩy động mô hình các số thực và phải tuân theo các quy luật giống như các số thực. Vấn đề với ngữ nghĩa số thực là chúng cực kỳ tốn kém để thực hiện. Mỗi khi nhân hai số n bit thì tích sẽ có 2n bit. Mỗi khi hai số n bit có số mũ cách đều nhau được thêm vào, số bit trong tổng là n + khoảng cách giữa các số mũ. Tổng có thể lên tới (emax - emin) + n bit hoặc khoảng 2·emax + n bit. Một thuật toán bao gồm hàng nghìn thao tác (chẳng hạn như giải một hệ thống tuyến tính) sẽ sớm hoạt động trên các số có nhiều bit có nghĩa và chậm một cách vô vọng. Việc thực hiện các hàm thư viện như sin, cos lại càng khó hơn vì giá trị của các hàm siêu việt này không phải là số hữu tỉ. Số học số nguyên chính xác thường được cung cấp bởi các hệ thống lisp và rất hữu ích cho một số vấn đề. Tuy nhiên, số học dấu phẩy động chính xác hiếm khi hữu ích

Thực tế là có những thuật toán hữu ích (như công thức tính tổng Kahan) khai thác thực tế là (x + y) + z

Nhân mảng javascript
x + (y + z) và

a
Nhân mảng javascript
b = (a + b)(1 +
Nhân mảng javascript
)

giữ (cũng như các giới hạn tương tự cho -, × và /). Vì các giới hạn này áp dụng cho hầu hết mọi phần cứng thương mại, nên sẽ thật ngu ngốc nếu các lập trình viên số bỏ qua các thuật toán như vậy và sẽ là vô trách nhiệm đối với người viết trình biên dịch khi phá hủy các thuật toán này bằng cách giả vờ rằng các biến dấu phẩy động có ngữ nghĩa số thực.

Xử lý ngoại lệ

Các chủ đề được thảo luận cho đến nay chủ yếu liên quan đến ý nghĩa hệ thống về độ chính xác và độ chính xác. Trình xử lý bẫy cũng đưa ra một số vấn đề hệ thống thú vị. Tiêu chuẩn IEEE khuyến nghị mạnh mẽ rằng người dùng có thể chỉ định trình xử lý bẫy cho từng loại trong số năm loại ngoại lệ và phần , đã đưa ra một số ứng dụng của trình xử lý bẫy do người dùng xác định. Trong trường hợp phép toán không hợp lệ và phép chia cho ngoại lệ bằng 0, trình xử lý phải được cung cấp toán hạng, nếu không, kết quả được làm tròn chính xác. Tùy thuộc vào ngôn ngữ lập trình đang được sử dụng, trình xử lý bẫy cũng có thể truy cập các biến khác trong chương trình. Đối với tất cả các trường hợp ngoại lệ, trình xử lý bẫy phải có khả năng xác định hoạt động nào đang được thực hiện và độ chính xác của đích đến

Tiêu chuẩn IEEE giả định rằng các hoạt động là nối tiếp về mặt khái niệm và khi xảy ra ngắt, có thể xác định hoạt động và các toán hạng của nó. Trên các máy có đường ống hoặc nhiều đơn vị số học, khi một ngoại lệ xảy ra, có thể không đủ nếu chỉ yêu cầu trình xử lý bẫy kiểm tra bộ đếm chương trình. Hỗ trợ phần cứng để xác định chính xác hoạt động nào bị mắc kẹt có thể cần thiết

Một vấn đề khác được minh họa bằng đoạn chương trình sau

     n = n/2
18
     n = n/2
19____00
     n = n/2
1

Giả sử phép nhân thứ hai đưa ra một ngoại lệ và trình xử lý bẫy muốn sử dụng giá trị của

     if (n==0) return u
3. Trên phần cứng có thể thực hiện cộng và nhân song song, trình tối ưu hóa có thể sẽ di chuyển thao tác cộng lên trước phép nhân thứ hai, để phép cộng có thể tiến hành song song với phép nhân đầu tiên. Do đó, khi bẫy nhân thứ hai,
     if (n==0) return u
     n = n/2
004 
     n = n/2
132 
     n = n/2
044 
     n = n/2
134 đã được thực hiện, có khả năng thay đổi kết quả của
     if (n==0) return u
3. Sẽ không hợp lý nếu trình biên dịch tránh loại tối ưu hóa này, bởi vì mọi hoạt động của dấu phẩy động đều có khả năng mắc bẫy và do đó hầu như tất cả các tối ưu hóa lập lịch trình hướng dẫn sẽ bị loại bỏ. Vấn đề này có thể tránh được bằng cách cấm trình xử lý bẫy truy cập trực tiếp vào bất kỳ biến nào của chương trình. Thay vào đó, trình xử lý có thể được cung cấp toán hạng hoặc kết quả dưới dạng đối số

Nhưng vẫn còn những vấn đề. trong đoạn

hai hướng dẫn cũng có thể được thực hiện song song. Nếu bẫy nhân, đối số của nó

     n = n/2
011 có thể đã bị ghi đè bởi phép cộng, đặc biệt vì phép cộng thường nhanh hơn phép nhân. Các hệ thống máy tính hỗ trợ tiêu chuẩn IEEE phải cung cấp một số cách để lưu giá trị của
     n = n/2
011, trong phần cứng hoặc bằng cách yêu cầu trình biên dịch tránh tình huống như vậy ngay từ đầu

W. Kahan đã đề xuất sử dụng thay thế trước thay vì xử lý bẫy để tránh những vấn đề này. Trong phương pháp này, người dùng chỉ định một ngoại lệ và giá trị mà anh ta muốn được sử dụng làm kết quả khi ngoại lệ xảy ra. Ví dụ: giả sử rằng trong mã tính toán (sin x)/x, người dùng quyết định rằng x = 0 rất hiếm nên sẽ cải thiện hiệu suất để tránh kiểm tra x = 0 và thay vào đó xử lý trường hợp này khi 0/ . Sử dụng trình xử lý bẫy IEEE, người dùng sẽ viết trình xử lý trả về giá trị 1 và cài đặt nó trước khi tính sin x/x. Sử dụng thay thế trước, người dùng sẽ chỉ định rằng khi xảy ra thao tác không hợp lệ, giá trị 1 sẽ được sử dụng. Kahan gọi đây là sự thay thế trước, bởi vì giá trị được sử dụng phải được chỉ định trước khi xảy ra ngoại lệ. Khi sử dụng trình xử lý bẫy, giá trị được trả về có thể được tính khi bẫy xảy ra

Ưu điểm của thay thế trước là nó có triển khai phần cứng đơn giản. Ngay sau khi loại ngoại lệ đã được xác định, nó có thể được sử dụng để lập chỉ mục cho bảng chứa kết quả mong muốn của thao tác. Mặc dù thay thế trước có một số thuộc tính hấp dẫn, nhưng việc chấp nhận rộng rãi tiêu chuẩn IEEE khiến nó khó có thể được các nhà sản xuất phần cứng triển khai rộng rãi

các chi tiết

Một số tuyên bố đã được đưa ra trong bài báo này liên quan đến các thuộc tính của số học dấu phẩy động. Bây giờ chúng tôi tiến hành chứng minh rằng dấu phẩy động không phải là ma thuật đen, mà đúng hơn là một chủ đề đơn giản mà các tuyên bố của nó có thể được xác minh bằng toán học. Phần này được chia thành ba phần. Phần đầu tiên trình bày giới thiệu về phân tích lỗi và cung cấp thông tin chi tiết cho phần. Phần thứ hai khám phá chuyển đổi nhị phân sang thập phân, điền vào một số khoảng trống từ phần. Phần thứ ba thảo luận về công thức tính tổng Kahan, được sử dụng làm ví dụ trong phần

Lỗi làm tròn

Trong cuộc thảo luận về lỗi làm tròn, người ta đã nói rằng một chữ số bảo vệ duy nhất là đủ để đảm bảo rằng phép cộng và phép trừ sẽ luôn chính xác (Định lý 2). Bây giờ chúng tôi tiến hành xác minh thực tế này. Định lý 2 có hai phần, một phần cho phép trừ và một phần cho phép cộng. Phần của phép trừ là

Định lý 9

Nếu x và y là các số dấu phẩy động dương ở định dạng có tham số

Nhân mảng javascript
và p và nếu phép trừ được thực hiện với p + 1 chữ số (i. e. một chữ số bảo vệ), thì sai số làm tròn tương đối trong kết quả nhỏ hơn

Nhân mảng javascript
e
Nhân mảng javascript
2e.

Bằng chứng

Hoán đổi x và y nếu cần sao cho x > y. Cũng vô hại khi chia tỷ lệ x và y sao cho x được biểu thị bằng x0. x1. xp - 1 ×
Nhân mảng javascript
0. Nếu y được biểu diễn dưới dạng y0. y1. yp-1, thì sự khác biệt là chính xác. Nếu y được biểu diễn bằng 0. y1. yp, thì chữ số bảo vệ đảm bảo rằng chênh lệch được tính toán sẽ là chênh lệch chính xác được làm tròn thành số dấu phẩy động, do đó, lỗi làm tròn tối đa là e. Nói chung, đặt y = 0. 0. 0yk + 1. yk + p và be y bị cắt ngắn thành p + 1 chữ số. Sau đó(15) y -< (
Nhân mảng javascript
- 1)(
Nhân mảng javascript
-p - 1 +
Nhân mảng javascript
-p - 2 +. +
Nhân mảng javascript
-p - k).
Từ định nghĩa của chữ số bảo vệ, giá trị tính toán của x - y là x - được làm tròn thành một số dấu phẩy động, nghĩa là (x -) +
Nhân mảng javascript
, where the rounding error
Nhân mảng javascript
satisfies(16) |
Nhân mảng javascript
.
Nhân mảng javascript
(
Nhân mảng javascript
/2)
Nhân mảng javascript
-p.
Sự khác biệt chính xác là x - y, vì vậy sai số là (x - y) - (x -+
Nhân mảng javascript
) =- y +
Nhân mảng javascript
. There are three cases. If x - y
Nhân mảng javascript
1 thì sai số tương đối bị giới hạn bởi(17)
Nhân mảng javascript
Nhân mảng javascript
-p [(
Nhân mảng javascript
- 1)( . +
Nhân mảng javascript
-1 + .. +
Nhân mảng javascript
-k) +
Nhân mảng javascript
/2] <
Nhân mảng javascript
-p(1 +
Nhân mảng javascript
.
Thứ hai, nếu x -< 1 thì
Nhân mảng javascript
= 0. Vì x - y nhỏ nhất có thể là
Nhân mảng javascript
> (
Nhân mảng javascript
- 1)(
Nhân mảng javascript
-1 +. +
Nhân mảng javascript
-k), trong đó
Nhân mảng javascript
=
Nhân mảng javascript
- 1,
trong trường hợp này .
Nhân mảng javascript
.
Trường hợp cuối cùng là khi x - y < 1 nhưng x -
Nhân mảng javascript
1. Cách duy nhất điều này có thể xảy ra là nếu x - = 1, trong trường hợp đó
Nhân mảng javascript
= 0. Nhưng nếu
Nhân mảng javascript
= 0, thì áp dụng, do đó một lần nữa lỗi tương đối được giới hạn bởi
Nhân mảng javascript
-p <
Nhân mảng javascript
-p( . z
Nhân mảng javascript
/2). z

Khi

Nhân mảng javascript
= 2, giới hạn chính xác là 2e và giới hạn này đạt được cho x= 1 + 22 - p và y = 21 - p - 21 - 2p trong giới hạn . Khi cộng các số cùng dấu, không cần thiết phải có chữ số bảo vệ để đạt được độ chính xác cao, như kết quả sau đây cho thấy.
Nhân mảng javascript
Nhân mảng javascript
. When adding numbers of the same sign, a guard digit is not necessary to achieve good accuracy, as the following result shows.

Định lý 10

Nếu x

Nhân mảng javascript
0 và y
Nhân mảng javascript
0, thì sai số tương đối trong phép tính x + y tối đa là 2
Nhân mảng javascript
, even if no guard digits are used.

Bằng chứng

Thuật toán cộng với k chữ số bảo vệ tương tự như thuật toán trừ. Nếu x 
Nhân mảng javascript
 y, dịch chuyển y sang phải cho đến khi các điểm cơ số của x và y thẳng hàng. Loại bỏ mọi chữ số đã dịch chuyển qua vị trí p + k. Tính tổng của hai số p + k chữ số này chính xác. Sau đó làm tròn đến p chữ số. Chúng tôi sẽ xác minh định lý khi không có số bảo vệ nào được sử dụng; . Không mất tính tổng quát khi giả sử rằng x
Nhân mảng javascript
y
Nhân mảng javascript
0 và x có dạng d. đ. d ×
Nhân mảng javascript
0. Đầu tiên, giả sử không có thực hiện. Sau đó, các chữ số bị dịch chuyển khỏi phần cuối của y có giá trị nhỏ hơn
Nhân mảng javascript
-p + 1 và tổng ít nhất là 1, vì vậy sai số tương đối nhỏ hơn
Nhân mảng javascript
-p+1/1 = 2e. If there is a carry out, then the error from shifting must be added to the rounding error of.

Tổng ít nhất là

Nhân mảng javascript
, vì vậy lỗi tương đối nhỏ hơn

Nhân mảng javascript
Nhân mảng javascript
2
Nhân mảng javascript
. z

Rõ ràng là kết hợp hai định lý này sẽ cho Định lý 2. Định lý 2 đưa ra sai số tương đối khi thực hiện một thao tác. So sánh lỗi làm tròn của x2 - y2 và (x + y) (x - y) yêu cầu biết lỗi tương đối của nhiều phép toán. Sai số tương đối của xy là

Nhân mảng javascript
1 = [(xy) - (x - y)]/(x - y), thỏa mãn.
Nhân mảng javascript
1.
Nhân mảng javascript
 2e. Hay viết theo cách khác

(19) xy = (x - y) (1 +
Nhân mảng javascript
1),.
Nhân mảng javascript
1.
Nhân mảng javascript
2e

Tương tự

(20) x
Nhân mảng javascript
y = (x + y) (1 +
Nhân mảng javascript
2),.
Nhân mảng javascript
2.
Nhân mảng javascript
2e

Giả sử rằng phép nhân được thực hiện bằng cách tính tích chính xác rồi làm tròn, sai số tương đối nhiều nhất là. 5 ul, vì vậy

(21) u
Nhân mảng javascript
v = uv (1 +
Nhân mảng javascript
3),.
Nhân mảng javascript
3.
Nhân mảng javascript
đ

cho bất kỳ số dấu phẩy động nào u và v. Đặt ba phương trình này lại với nhau (đặt u = xy và v = x

Nhân mảng javascript
y) cho kết quả

(22) (xy)
Nhân mảng javascript
(x
Nhân mảng javascript
y) = (x - y) (1 +
Nhân mảng javascript
1) (x + y) (1 +
Nhân mảng javascript
2) (1 +
Nhân mảng javascript
3)

Vì vậy, lỗi tương đối phát sinh khi tính toán (x - y) (x + y) là

(23)
Nhân mảng javascript

Lỗi tương đối này bằng

Nhân mảng javascript
1 +
Nhân mảng javascript
2 +
Nhân mảng javascript
3 +
Nhân mảng javascript
1
Nhân mảng javascript
2 +
Nhân mảng javascript
1
Nhân mảng javascript
3 +
Nhân mảng javascript
2
Nhân mảng javascript
3 +
Nhân mảng javascript
1
Nhân mảng javascript
2
Nhân mảng javascript
3, which is bounded by 5
Nhân mảng javascript
+ 8
Nhân mảng javascript
2. In other words, the maximum relative error is about 5 rounding errors (since e is a small number, e2 is almost negligible).

Một phân tích tương tự của (x

Nhân mảng javascript
x)(y
Nhân mảng javascript
y) không thể dẫn đến một giá trị nhỏ cho lỗi tương đối, bởi vì khi hai . Một cách khác để thấy điều này là thử và sao chép bản phân tích đã hoạt động trên (xy)
Nhân mảng javascript
(x
Nhân mảng javascript
y), mang lại kết quả

(x
Nhân mảng javascript
x)(y
Nhân mảng javascript
y) = [x2(1 +
Nhân mảng javascript
1) - y2
Nhân mảng javascript
2)] (1 +
Nhân mảng javascript
3)
= ((x2 - y2) (1 +
Nhân mảng javascript
1) + (
Nhân mảng javascript
1 -
Nhân mảng javascript
2)y2) (1 +
Nhân mảng javascript
3)

Khi x và y ở gần nhau, thuật ngữ lỗi (

Nhân mảng javascript
1 -
Nhân mảng javascript
2)y2 có thể lớn bằng kết quả x2 - y2. Những tính toán này chính thức biện minh cho tuyên bố của chúng tôi rằng (x - y) (x + y) chính xác hơn x2 - y2.

Tiếp theo chúng ta chuyển sang phân tích công thức tính diện tích tam giác. Để ước tính lỗi tối đa có thể xảy ra khi tính toán với , thực tế sau đây sẽ cần thiết

Định lý 11

Nếu phép trừ được thực hiện với một chữ số bảo vệ và y/2
Nhân mảng javascript
x
Nhân mảng javascript
2y, thì x - y được tính chính xác.

Bằng chứng

Lưu ý rằng nếu x và y có cùng số mũ thì chắc chắn xy chính xác. Mặt khác, từ điều kiện của định lý, các số mũ có thể khác nhau nhiều nhất 1. Chia tỷ lệ và hoán đổi x và y nếu cần sao cho 0
Nhân mảng javascript
y
Nhân mảng javascript
x và x được biểu diễn dưới dạng x0. x1. xp - 1 và y là 0. y1. y P. Khi đó thuật toán tính xy sẽ tính chính xác x - y và làm tròn thành số dấu phẩy động. Nếu sự khác biệt có dạng 0. d1. dp, sự khác biệt sẽ có độ dài p chữ số và không cần làm tròn. Vì x
Nhân mảng javascript
2y, x - y
Nhân mảng javascript
 y và vì y có dạng 0. d1. dp, x - y cũng vậy. z

Khi

Nhân mảng javascript
> 2, giả thuyết của Định lý 11 không thể được thay thế bởi y/
Nhân mảng javascript
Nhân mảng javascript
Nhân mảng javascript
Nhân mảng javascript
y; . Việc phân tích lỗi trong (x - y) (x + y), ngay sau bằng chứng của Định lý 10, đã sử dụng thực tế là lỗi tương đối trong các phép tính cộng và trừ cơ bản là nhỏ (cụ thể là phương trình và ). Đây là loại phân tích lỗi phổ biến nhất. Tuy nhiên, việc phân tích công thức đòi hỏi nhiều hơn thế, cụ thể là Định lý 11, như chứng minh sau đây sẽ chỉ ra.
Nhân mảng javascript
x
Nhân mảng javascript
2y is still necessary. The analysis of the error in (x - y) (x + y), immediately following the proof of Theorem 10, used the fact that the relative error in the basic operations of addition and subtraction is small (namely equations and ). This is the most common kind of error analysis. However, analyzing formula requires something more, namely Theorem 11, as the following proof will show.

Định lý 12

Nếu phép trừ sử dụng chữ số bảo vệ và nếu a, b và c là các cạnh của tam giác (a 
Nhân mảng javascript
 b
Nhân mảng javascript
 c), thì . 005.
Nhân mảng javascript
, provided e < .005.

Bằng chứng

Hãy xem xét từng yếu tố một. Từ Định lý 10, b 
Nhân mảng javascript
 c = (b + c) (1 + 
Nhân mảng javascript
1), trong đó
Nhân mảng javascript
1 là hệ thức tương đối .
Nhân mảng javascript
1.
Nhân mảng javascript
2
Nhân mảng javascript
. Khi đó giá trị của thừa số thứ nhất là(a
Nhân mảng javascript
(b
Nhân mảng javascript
c)) = (a + (b
Nhân mảng javascript
c) .
Nhân mảng javascript
2) = (a + (b + c) (1 +
Nhân mảng javascript
1))(1 +
Nhân mảng javascript
2),
and thus(a + b + c) (1 - 2
Nhân mảng javascript
)2
Nhân mảng javascript
[a + (b + c) (1 - 2
Nhân mảng javascript
)] · (1-2
Nhân mảng javascript
)
Nhân mảng javascript
a
Nhân mảng javascript
(b
Nhân mảng javascript
c)
Nhân mảng javascript
[a + (b + c) (1 + 2
Nhân mảng javascript
)] (1 + 2
Nhân mảng javascript
)
Nhân mảng javascript
(a + b + c) (1 + 2
Nhân mảng javascript
)2
This means that there is an
Nhân mảng javascript
1 so that(24) (a
Nhân mảng javascript
(b
Nhân mảng javascript
c)) = (a + b + c) (1 +
Nhân mảng javascript
1)2, |
Nhân mảng javascript
1.
Nhân mảng javascript
2
Nhân mảng javascript
.
Số hạng tiếp theo liên quan đến phép trừ có khả năng gây thảm họa của c và a  
     n = n/2
132, vì ab có thể có lỗi làm tròn. Bởi vì a, b và c là các cạnh của một tam giác, a
Nhân mảng javascript
b+ c, và kết hợp điều này với thứ tự c
Nhân mảng javascript
b
Nhân mảng javascript
a gives a
Nhân mảng javascript
b + c
Nhân mảng javascript
2b
Nhân mảng javascript
2a. So a - b satisfies the conditions of Theorem 11. This means that a - b = ab is exact, hence c(a - b) is a harmless subtraction which can be estimated from Theorem 9 to be(25) (c(ab)) = (c - (a - b)) (1 +
Nhân mảng javascript
2), |
Nhân mảng javascript
2.
Nhân mảng javascript
2
Nhân mảng javascript

Số hạng thứ ba là tổng của hai đại lượng dương chính xác, vì vậy(26) (c
Nhân mảng javascript
(ab)) .
Nhân mảng javascript
3), |
Nhân mảng javascript
3.
Nhân mảng javascript
2
Nhân mảng javascript

Cuối cùng, số hạng cuối cùng là (27) (a
Nhân mảng javascript
(bc)) = (a + (b - .
Nhân mảng javascript
4)2, |
Nhân mảng javascript
4.
Nhân mảng javascript
2
Nhân mảng javascript
,
sử dụng cả Định lý 9 và Định lý 10. Nếu phép nhân được giả định là làm tròn chính xác, sao cho x
Nhân mảng javascript
y = xy(1 +
Nhân mảng javascript
) với.
Nhân mảng javascript
.
Nhân mảng javascript
Nhân mảng javascript
, sau đó kết hợp , , và cho (a
Nhân mảng javascript
(b
Nhân mảng javascript
c)) (c(ab)) (c < . Một số người viết đơn giản bỏ qua thuật ngữ O(e2), nhưng rất dễ giải thích cho nó. Viết (1 + 2
Nhân mảng javascript
(ab)) (a
Nhân mảng javascript
(bc))
Nhân mảng javascript
(a + (b + c)) (c - (a - b)) (c + (a - b)) (a + (b - c)) E
whereE = (1 +
Nhân mảng javascript
1)2 (1 +
Nhân mảng javascript
2) (1 +
Nhân mảng javascript
3) (1 +
Nhân mảng javascript
4)2 (1 +
Nhân mảng javascript
1)(1 +
Nhân mảng javascript
2) (1 +
Nhân mảng javascript
3)
An upper bound for E is (1 + 2
Nhân mảng javascript
)6(1 +
Nhân mảng javascript
)3, which expands out to 1 + 15
Nhân mảng javascript
 + O(
Nhân mảng javascript
2). Some writers simply ignore the O(e2) term, but it is easy to account for it. Writing (1 + 2
Nhân mảng javascript
)6(1 +
Nhân mảng javascript
)3 = 1 + 15
Nhân mảng javascript
+
Nhân mảng javascript
R(
Nhân mảng javascript
), R(
Nhân mảng javascript
) is a polynomial in e with positive coefficients, so it is an increasing function of
Nhân mảng javascript
. Since R(.005) = .505, R(
Nhân mảng javascript
) < 1 cho tất cả
Nhân mảng javascript
Nhân mảng javascript
 (1 + 2
Nhân mảng javascript
)6(1 + 
Nhân mảng javascript
)3 
Nhân mảng javascript
. To get a lower bound on E, note that 1 - 15
Nhân mảng javascript
 - 
Nhân mảng javascript
R(
Nhân mảng javascript
) < E, v.v. . 005, 1 - 16
Nhân mảng javascript
< .005, 1 - 16
Nhân mảng javascript
< (1 - 2
Nhân mảng javascript
)6(1 -
Nhân mảng javascript
)3. Kết hợp hai giới hạn này mang lại 1 - 16
Nhân mảng javascript
< E
Nhân mảng javascript
. Do đó, sai số tương đối nhiều nhất là 16
Nhân mảng javascript
. z

Định lý 12 chắc chắn chỉ ra rằng không có sự triệt tiêu thảm khốc trong công thức. Vì vậy, mặc dù không cần thiết phải chứng minh công thức là ổn định về mặt số, nhưng việc có một giới hạn cho toàn bộ công thức là thỏa mãn, đó là điều mà Định lý 3 đưa ra

Chứng minh Định lý 3

Letq = (a + (b + c)) (c - (a - b)) (c + (a - b)) (a + (b - c))
andQ = (a
Nhân mảng javascript
(b
Nhân mảng javascript
c))
Nhân mảng javascript
(c(ab))
Nhân mảng javascript
(c
Nhân mảng javascript
(ab))
Nhân mảng javascript
(a
Nhân mảng javascript
(bc)).
Sau đó, Định lý 12 chứng tỏ rằng Q = q(1 +
Nhân mảng javascript
), với
Nhân mảng javascript
Nhân mảng javascript
16
Nhân mảng javascript
. It is easy to check that(28)
được cung cấp
Nhân mảng javascript
Nhân mảng javascript
. 04/(. 52)2
Nhân mảng javascript
. 15, và kể từ đó.
Nhân mảng javascript
.
Nhân mảng javascript
16
Nhân mảng javascript
Nhân mảng javascript
16(. 005) =. 08,
Nhân mảng javascript
thỏa mãn điều kiện. Như vậy,
với.
Nhân mảng javascript
1.
Nhân mảng javascript
. 52.
Nhân mảng javascript
.
Nhân mảng javascript
8. 5
Nhân mảng javascript
. Nếu căn bậc hai được tính trong phạm vi. 5 ulp, thì lỗi khi tính toán là (1 + 
Nhân mảng javascript
1)(1 +
Nhân mảng javascript
2), với.
Nhân mảng javascript
2.
Nhân mảng javascript
Nhân mảng javascript
. Nếu
Nhân mảng javascript
= 2 thì không mắc lỗi nữa khi chia cho 4. Ngược lại, thêm một thừa số 1 +
Nhân mảng javascript
3 với.
Nhân mảng javascript
3.
Nhân mảng javascript
 
Nhân mảng javascript
là cần thiết cho phép chia và sử dụng phương pháp trong chứng minh Định lý 12, giới hạn sai số cuối cùng của (1 +
Nhân mảng javascript
1) (1 +
Nhân mảng javascript
2) (1 +
Nhân mảng javascript
3) is dominated by 1 +
Nhân mảng javascript
4, with |
Nhân mảng javascript
4.
Nhân mảng javascript
11
Nhân mảng javascript
. z

Để làm cho lời giải thích heuristic ngay sau phát biểu của Định lý 4 trở nên chính xác, định lý tiếp theo mô tả mức độ gần đúng của µ(x) với một hằng số

Định lý 13

Nếu µ(x) = ln(1 + x)/x thì với 0
Nhân mảng javascript
x
Nhân mảng javascript
,
Nhân mảng javascript
µ(x)
Nhân mảng javascript
1 and the derivative satisfies |µ'(x)| 
Nhân mảng javascript
.

Bằng chứng

Lưu ý rằng µ(x) = 1 - x/2 + x2/3 -. là một chuỗi xen kẽ với các số hạng giảm dần, vì vậy với x
Nhân mảng javascript
1, µ(x)
Nhân mảng javascript
1 - x/2
Nhân mảng javascript
1 . Thậm chí dễ dàng hơn để thấy rằng vì chuỗi của µ là xen kẽ, nên µ(x)
Nhân mảng javascript
1. Dãy Taylor của µ'(x) cũng xen kẽ, và nếu x
Nhân mảng javascript
có các số hạng giảm dần, thì -
Nhân mảng javascript
µ'(x)
Nhân mảng javascript
-+ 2x/3, or -
Nhân mảng javascript
µ'(x)
Nhân mảng javascript
0, thus |µ'(x)|
Nhân mảng javascript
. z

Chứng minh Định lý 4

Vì chuỗi Taylor cho ln
Nhân mảng javascript

là một chuỗi xen kẽ, 0 < x - ln(1 + x) < x2/2, sai số tương đối phát sinh khi tính gần đúng ln(1 + . Nếu 1
Nhân mảng javascript
x = 1 thì. x.
Nhân mảng javascript
, do đó, lỗi tương đối bị giới hạn bởi
Nhân mảng javascript
/2. Khi 1
Nhân mảng javascript
x
Nhân mảng javascript
1, xác định qua 1
Nhân mảng javascript
x = 1 +. Khi đó do 0
Nhân mảng javascript
x < 1 nên (1
Nhân mảng javascript
x)1 =. Nếu phép chia và logarit được tính trong ulp, thì giá trị được tính của biểu thức ln(1 + x)/((1 + x) - 1) là(29)(1 +
Nhân mảng javascript
1
Nhân mảng javascript
2) =(1 +
Nhân mảng javascript
1) (1 +
Nhân mảng javascript
2) = µ() (1 +
Nhân mảng javascript
1) (1 +
Nhân mảng javascript
2)

ở đâu.

Nhân mảng javascript
1.
Nhân mảng javascript
Nhân mảng javascript
và.
Nhân mảng javascript
2.
Nhân mảng javascript
Nhân mảng javascript
. Để ước tính µ(), hãy sử dụng định lý giá trị trung bình cho biết

(30) µ() - µ(x) = (- x)µ'(
Nhân mảng javascript
)
đối với một số
Nhân mảng javascript
between x and. From the definition of, it follows that |- x|
Nhân mảng javascript
Nhân mảng javascript
, và kết hợp điều này với Định lý 13 sẽ cho. µ() - µ(x).
Nhân mảng javascript
Nhân mảng javascript
/2 hoặc. µ()/µ(x) - 1.
Nhân mảng javascript
Nhân mảng javascript
/(2. µ(x). )
Nhân mảng javascript
Nhân mảng javascript
nghĩa là µ() = µ(x) (1 +
Nhân mảng javascript
3), với.
Nhân mảng javascript
3.
Nhân mảng javascript
Nhân mảng javascript
. Cuối cùng, nhân với x đưa ra kết quả cuối cùng là
Nhân mảng javascript
4, vì vậy giá trị được tính của x·ln(1 
Nhân mảng javascript
x)/((1
Nhân mảng javascript
x)1)


Dễ dàng kiểm tra xem nếu
Nhân mảng javascript
< 0. 1 thì(1 +
Nhân mảng javascript
1) (1 +
Nhân mảng javascript
2) (1 +
Nhân mảng javascript
3) (1 + Nhân mảng javascript4) = 1 + 
Nhân mảng javascript
,

với.

Nhân mảng javascript
.
Nhân mảng javascript
5
Nhân mảng javascript
. z

Một ví dụ thú vị về phân tích lỗi sử dụng công thức , , và xảy ra trong công thức bậc hai. Phần , đã giải thích cách viết lại phương trình sẽ loại bỏ khả năng hủy bỏ gây ra bởi phép toán ±. Nhưng có một khả năng triệt tiêu khác có thể xảy ra khi tính toán d = b2 - 4ac. Điều này không thể được loại bỏ bằng cách sắp xếp lại công thức đơn giản. Nói một cách đại khái, khi b2

Nhân mảng javascript
4ac, lỗi làm tròn có thể làm ô nhiễm tới một nửa chữ số trong các căn được tính theo công thức bậc hai. Đây là một bằng chứng không chính thức (một cách tiếp cận khác để ước lượng sai số trong công thức bậc hai xuất hiện trong Kahan [1972]).

Nếu b2

Nhân mảng javascript
4ac, lỗi làm tròn có thể ảnh hưởng đến một nửa chữ số trong các căn được tính theo công thức bậc hai.

Bằng chứng. Viết (b

Nhân mảng javascript
b)(4a
Nhân mảng javascript
c) = (b2(1 +
Nhân mảng javascript
1) - 4ac(1 + < .
Nhân mảng javascript
2)) (1 +
Nhân mảng javascript
3), where |
Nhân mảng javascript
i.
Nhân mảng javascript
 
Nhân mảng javascript
. Sử dụng d = b2 - 4ac, điều này có thể được viết lại thành (d(1 ​​+
Nhân mảng javascript
1) - 4ac(
Nhân mảng javascript
2 -
Nhân mảng javascript
1)) (1 +
Nhân mảng javascript
3). To get an estimate for the size of this error, ignore second order terms in
Nhân mảng javascript
i, trong trường hợp đó, lỗi tuyệt đối là d(
Nhân mảng javascript
1 +
Nhân mảng javascript
3) - 4ac
Nhân mảng javascript
4, where |
Nhân mảng javascript
4. =.
Nhân mảng javascript
1 - 
Nhân mảng javascript
2.
Nhân mảng javascript
2
Nhân mảng javascript
. Vì, số hạng đầu tiên d(
Nhân mảng javascript
1 +
Nhân mảng javascript
3) có thể bỏ qua. Để ước tính số hạng thứ hai, hãy sử dụng công thức ax2 + bx + c = a(x - r1) (x - r2), do đó ar1r2 = c. Vì b2
Nhân mảng javascript
4ac nên r1
Nhân mảng javascript
r2 nên số hạng sai số thứ hai là. Do đó, giá trị tính toán của là

.

sự bất bình đẳng

chỉ ra rằng

,

ở đâu

,

vì vậy lỗi tuyệt đối ina là về. Vì

Nhân mảng javascript
4
Nhân mảng javascript
Nhân mảng javascript
-p, và do đó, lỗi tuyệt đối của việc hủy nửa dưới của các bit của gốc r1
Nhân mảng javascript
r2. Nói cách khác, vì phép tính căn liên quan đến tính toán với và biểu thức này không có các bit có ý nghĩa ở vị trí tương ứng với nửa bậc dưới của ri, nên các bit bậc dưới của ri không thể có nghĩa. z

Cuối cùng, chúng ta chuyển sang chứng minh Định lý 6. Nó dựa trên thực tế sau đây, được chứng minh trong phần

Định lý 14

Cho 0 < k < p và đặt m =
Nhân mảng javascript
k + 1 và giả sử rằng các phép toán dấu phẩy động được làm tròn chính xác. Khi đó (m
Nhân mảng javascript
x)(m
Nhân mảng javascript
xx) chính xác bằng x được làm tròn thành p - k chữ số có nghĩa. Chính xác hơn, x được làm tròn bằng cách lấy ý nghĩa của x, tưởng tượng một điểm cơ số nằm ngay bên trái của k chữ số có nghĩa nhỏ nhất và làm tròn thành một số nguyên.

Chứng minh Định lý 6

Theo Định lý 14, xh là x được làm tròn thành p - k =số. Nếu không có thực hiện thì chắc chắn xh có thể được biểu diễn bằng các chữ số có nghĩa. Giả sử có một thực hiện. Nếu x = x0. x1. xp - 1 ×
Nhân mảng javascript
e, sau đó làm tròn thêm 1 vào xp - k - 1 và cách duy nhất để có thể thực hiện là nếu xp - k - 1 =
Nhân mảng javascript
- 1, but then the low order digit of xh is 1 + xp - k- 1 = 0, and so again xh is representable indigits.To deal with xl, scale x to be an integer satisfying
Nhân mảng javascript
p - 1
Nhân mảng javascript
x
Nhân mảng javascript
Nhân mảng javascript
p - 1. Gọi p - k chữ số bậc cao của x, và k chữ số bậc thấp. Có ba trường hợp cần xem xét. Nếu, sau đó làm tròn x đến p - k vị trí giống như cắt và, và. Vì có nhiều nhất k chữ số, nếu p chẵn thì có nhiều nhất k ==chữ số. Mặt khác,
Nhân mảng javascript
= 2 và có thể biểu diễn bằng k - 1
Nhân mảng javascript
bit quan trọng. Trường hợp thứ hai là khi, và sau đó phép tính xh liên quan đến việc làm tròn số, vì vậy xh =+
Nhân mảng javascript
k và xl = x - xh = x ---
Nhân mảng javascript
k = . Một lần nữa, có tối đa k chữ số, vì vậy có thể biểu diễn bằng
Nhân mảng javascript
k. Once again,has at most k digits, so is representable with
Nhân mảng javascript
p/2
Nhân mảng javascript
chữ số. Cuối cùng, nếu = (
Nhân mảng javascript
/2)
Nhân mảng javascript
k - 1, thì xh =or + 
Nhân mảng javascript
k tùy thuộc vào việc có . Vậy xl là (
Nhân mảng javascript
/2)
Nhân mảng javascript
k - 1 hoặc (
Nhân mảng javascript
/2)
Nhân mảng javascript
k - 1 - 
Nhân mảng javascript
k = -
Nhân mảng javascript
k/2, both of which are represented with 1 digit. z

Định lý 6 đưa ra cách biểu diễn tích của hai số chính xác đang hoạt động chính xác dưới dạng tổng. Có một công thức đồng hành để biểu thị một tổng chính xác. Nếu. x.

Nhân mảng javascript
. y. thì x + y = (x
Nhân mảng javascript
y) + (x(x
Nhân mảng javascript
y))
Nhân mảng javascript
y [Dekker 1971; . 2. 2]. Tuy nhiên, khi sử dụng các phép toán làm tròn chính xác, công thức này chỉ đúng với
Nhân mảng javascript
= 2 chứ không đúng với
Nhân mảng javascript
= 10 như ví dụ x =. 99998, y =. 99997 chương trình.

Chuyển đổi nhị phân sang thập phân

Vì độ chính xác đơn có p = 24 và 224 < 108, bạn có thể mong đợi rằng việc chuyển đổi số nhị phân thành 8 chữ số thập phân sẽ đủ để khôi phục số nhị phân ban đầu. Tuy nhiên, đây không phải là trường hợp

Định lý 15

Khi một số nhị phân có độ chính xác đơn của IEEE được chuyển đổi thành số thập phân có tám chữ số gần nhất, không phải lúc nào cũng có thể khôi phục duy nhất số nhị phân từ số thập phân. Tuy nhiên, nếu chín chữ số thập phân được sử dụng, thì việc chuyển đổi số thập phân thành số nhị phân gần nhất sẽ phục hồi số dấu phẩy động ban đầu

Bằng chứng

Các số nhị phân có độ chính xác đơn nằm trong nửa khoảng mở [103, 210) = [1000, 1024) có 10 bit ở bên trái điểm nhị phân và 14 bit ở bên phải điểm nhị phân. Như vậy có (210 - 103)214 = 393.216 số nhị phân khác nhau trong khoảng đó. Nếu số thập phân được biểu diễn bằng 8 chữ số thì có (210 - 103)104 = 240.000 số thập phân trong cùng một khoảng. Không có cách nào mà 240.000 số thập phân có thể đại diện cho 393.216 số nhị phân khác nhau. Vì vậy, 8 chữ số thập phân không đủ để biểu thị duy nhất từng số nhị phân chính xác duy nhất. Để chỉ ra rằng 9 chữ số là đủ, nó đủ để chỉ ra rằng khoảng cách giữa các số nhị phân luôn lớn hơn khoảng cách giữa các số thập phân. Điều này sẽ đảm bảo rằng với mỗi số thập phân N, khoảng[N -ulp, N +ulp]
chứa nhiều nhất một số nhị phân. Do đó, mỗi số nhị phân làm tròn thành một số thập phân duy nhất, đến lượt nó làm tròn thành một số nhị phân duy nhất. Để chỉ ra rằng khoảng cách giữa các số nhị phân luôn lớn hơn khoảng cách giữa các số thập phân, hãy xem xét một khoảng [10n, 10n + 1]. Trong khoảng này, khoảng cách giữa các số thập phân liên tiếp là 10(n + 1) - 9. Trên [10n, 2m], trong đó m là số nguyên nhỏ nhất sao cho 10n < 2m, khoảng cách giữa các số nhị phân là 2m - 24 và khoảng cách càng lớn dần trong khoảng. Do đó, chỉ cần kiểm tra rằng 10(n + 1) - 9 < 2m - 24 là đủ. Nhưng thực tế, vì 10n < 2m nên 10(n + 1) - 9 = 10n10-8 < 2m10-8 < 2m2-24. z

Đối số tương tự được áp dụng cho độ chính xác kép cho thấy cần có 17 chữ số thập phân để khôi phục số có độ chính xác kép

Chuyển đổi nhị phân-thập phân cũng cung cấp một ví dụ khác về việc sử dụng cờ. Nhớ lại từ phần , rằng để khôi phục số nhị phân từ phần mở rộng thập phân của nó, chuyển đổi thập phân sang nhị phân phải được tính toán chính xác. Việc chuyển đổi đó được thực hiện bằng cách nhân các đại lượng N với 10. P. (cả hai đều chính xác nếu p <13) ở độ chính xác mở rộng đơn và sau đó làm tròn số này thành độ chính xác đơn (hoặc chia nếu p <0; cả hai trường hợp đều giống nhau). Tất nhiên việc tính toán N · 10. P. không thể chính xác; . P. ) phải chính xác, trong đó làm tròn từ độ chính xác mở rộng đơn đến độ chính xác đơn. Để biết tại sao nó có thể không chính xác, hãy lấy trường hợp đơn giản là = 10, p = 2 cho đơn lẻ và p = 3 cho mở rộng đơn lẻ. Nếu sản phẩm là 12. 51, thì số này sẽ được làm tròn thành 12. 5 như một phần của thao tác nhân mở rộng đơn lẻ. Làm tròn đến độ chính xác duy nhất sẽ cho 12. Nhưng câu trả lời đó không chính xác, bởi vì làm tròn sản phẩm đến độ chính xác duy nhất sẽ cho 13. Lỗi là do làm tròn hai lần.

Nhân mảng javascript
= 10, p = 2 for single, and p = 3 for single-extended. If the product is to be 12.51, then this would be rounded to 12.5 as part of the single-extended multiply operation. Rounding to single precision would give 12. But that answer is not correct, because rounding the product to single precision should give 13. The error is due to double rounding.

Bằng cách sử dụng cờ IEEE, có thể tránh làm tròn hai lần như sau. Lưu giá trị hiện tại của cờ không chính xác, rồi đặt lại. Đặt chế độ làm tròn thành làm tròn về 0. Sau đó thực hiện phép nhân N · 10. P. Lưu trữ giá trị mới của cờ không chính xác trong

     n = n/2
139 và khôi phục chế độ làm tròn và cờ không chính xác. Nếu
     n = n/2
139 là 0 thì N · 10. P. là chính xác, nên tròn(N · 10. P. ) sẽ chính xác đến bit cuối cùng. Nếu
     n = n/2
139 là 1, thì một số chữ số đã bị cắt bớt, vì làm tròn số 0 luôn bị cắt bớt. Ý nghĩa của sản phẩm sẽ giống như 1. b1. b22b23. b31. Lỗi làm tròn kép có thể xảy ra nếu b23. b31 = 10. 0. Một cách đơn giản để giải thích cho cả hai trường hợp là thực hiện một
     n = n/2
142 hợp lý của
     n = n/2
139 với b31. Sau đó làm tròn (N · 10. P. ) sẽ được tính toán chính xác trong mọi trường hợp

Lỗi trong tổng kết

Phần , đã đề cập đến vấn đề tính toán chính xác các khoản tiền rất dài. Cách tiếp cận đơn giản nhất để cải thiện độ chính xác là tăng gấp đôi độ chính xác. Để có ước tính sơ bộ về việc nhân đôi độ chính xác sẽ cải thiện độ chính xác của một phép cộng bao nhiêu, hãy đặt s1 = x1, s2 = s1 

Nhân mảng javascript
 x2. , si = si - 1
Nhân mảng javascript
xi. Khi đó si = (1 + 
Nhân mảng javascript
i) (si - 1 + xi), trong đó
Nhân mảng javascript
Nhân mảng javascript
i
Nhân mảng javascript
Nhân mảng javascript
Nhân mảng javascript
và bỏ qua bậc hai
Nhân mảng javascript
i gives

(31)
Nhân mảng javascript

Đẳng thức đầu tiên của cho thấy rằng giá trị được tính toán của of giống như thể một phép tính tổng chính xác được thực hiện trên các giá trị bị xáo trộn của xj. Số hạng đầu x1 bị nhiễu bởi n

Nhân mảng javascript
, số hạng cuối xn chỉ bị nhiễu bởi
Nhân mảng javascript
. Đẳng thức thứ hai trong chỉ ra rằng số hạng sai số bị giới hạn bởi. Nhân đôi độ chính xác có tác dụng bình phương
Nhân mảng javascript
. Nếu tính tổng được thực hiện ở định dạng độ chính xác kép của IEEE, 1/
Nhân mảng javascript
Nhân mảng javascript
1016, sao cho bất kỳ giá trị hợp lý nào của n. Do đó, việc tăng gấp đôi độ chính xác sẽ làm nhiễu tối đa n
Nhân mảng javascript
và thay đổi nó thành. Do đó, giới hạn sai số 2
Nhân mảng javascript
của công thức tính tổng Kahan (Định lý 8) không tốt bằng việc sử dụng độ chính xác kép, mặc dù nó tốt hơn nhiều so với độ chính xác đơn.

Để có lời giải thích trực quan về lý do tại sao công thức tính tổng Kahan hoạt động, hãy xem sơ đồ sau của quy trình

Nhân mảng javascript

Mỗi khi thêm một lệnh tổng, sẽ có một hệ số hiệu chỉnh C sẽ được áp dụng cho vòng lặp tiếp theo. Vì vậy, trước tiên hãy trừ hiệu chỉnh C được tính trong vòng lặp trước từ Xj, cho tổng kết quả đã sửa Y. Sau đó thêm summand này vào tổng đang chạy S. Các bit bậc thấp của Y (cụ thể là Yl) bị mất trong tổng. Tiếp theo tính toán các bit bậc cao của Y bằng cách tính toán T - S. Khi Y bị trừ đi, các bit bậc thấp của Y sẽ được phục hồi. Đây là những bit đã bị mất trong tổng đầu tiên trong sơ đồ. Chúng trở thành hệ số điều chỉnh cho vòng lặp tiếp theo. Một chứng minh chính thức của Định lý 8, lấy từ Knuth [1981] trang 572, xuất hiện trong phần. "

Tóm lược

Không có gì lạ khi các nhà thiết kế hệ thống máy tính bỏ qua các phần của hệ thống liên quan đến dấu chấm động. Điều này có lẽ là do thực tế là dấu phẩy động được chú ý rất ít (nếu có) trong chương trình khoa học máy tính. Đến lượt nó, điều này đã gây ra niềm tin rõ ràng phổ biến rằng dấu phẩy động không phải là một chủ đề có thể định lượng được, và do đó, không cần phải lo lắng về các chi tiết của phần cứng và phần mềm xử lý nó

Bài báo này đã chứng minh rằng có thể lập luận chặt chẽ về dấu phẩy động. Ví dụ: các thuật toán dấu phẩy động liên quan đến hủy bỏ có thể được chứng minh là có lỗi tương đối nhỏ nếu phần cứng bên dưới có chữ số bảo vệ và có một thuật toán hiệu quả để chuyển đổi nhị phân-thập phân có thể được chứng minh là không thể đảo ngược, với điều kiện là độ chính xác mở rộng là . Nhiệm vụ xây dựng phần mềm dấu phẩy động đáng tin cậy được thực hiện dễ dàng hơn nhiều khi hệ thống máy tính cơ bản hỗ trợ dấu phẩy động. Ngoài hai ví dụ vừa được đề cập (số bảo vệ và độ chính xác mở rộng), phần của bài báo này có các ví dụ khác nhau, từ thiết kế tập lệnh đến tối ưu hóa trình biên dịch minh họa cách hỗ trợ dấu phẩy động tốt hơn.

Sự chấp nhận ngày càng tăng của tiêu chuẩn dấu chấm động IEEE có nghĩa là các mã sử dụng các tính năng của tiêu chuẩn ngày càng trở nên di động hơn. Phần , đã đưa ra nhiều ví dụ minh họa cách sử dụng các tính năng của tiêu chuẩn IEEE để viết mã dấu phẩy động thực tế

Sự nhìn nhận

Bài viết này được lấy cảm hứng từ một khóa học do W. Kahan tại Sun Microsystems từ tháng 5 đến tháng 7 năm 1988, được tổ chức rất khéo léo bởi David Hough của Sun. Hy vọng của tôi là cho phép những người khác tìm hiểu về sự tương tác của hệ thống máy tính và dấu chấm động mà không cần phải thức dậy kịp thời để tham dự 8. 00 một. m. bài giảng. Xin cảm ơn Kahan và nhiều đồng nghiệp của tôi tại Xerox PARC (đặc biệt là John Gilbert) đã đọc bản thảo của bài báo này và đưa ra nhiều nhận xét hữu ích. Nhận xét từ Paul Hilfinger và một trọng tài ẩn danh cũng giúp cải thiện phần trình bày

Người giới thiệu

Ồ, Alfred V. , Sethi, R. , và Ullman J. Đ. 1986. Trình biên dịch. Nguyên tắc, Kỹ thuật và Công cụ, Addison-Wesley, Reading, MA

ANSI 1978. Ngôn ngữ lập trình tiêu chuẩn quốc gia Mỹ FORTRAN, tiêu chuẩn ANSI X3. 9-1978, Viện Tiêu chuẩn Quốc gia Mỹ, New York, NY

Barnett, David 1987. Môi trường dấu phẩy động di động, bản thảo chưa xuất bản

Nâu, W. S. 1981. Một mô hình tính toán dấu phẩy động đơn giản nhưng thực tế, ACM Trans. về môn Toán. Phần mềm 7(4), trang. 445-480

Cody, W. J et. tất cả. 1984. Tiêu chuẩn không phụ thuộc vào cơ số và độ dài từ được đề xuất cho Số học dấu phẩy động, IEEE Micro 4(4), trang. 86-100

Cody, W. J. 1988. Tiêu chuẩn dấu phẩy động -- Lý thuyết và thực hành, trong "Độ tin cậy trong máy tính. vai trò của các phương pháp khoảng thời gian trong tính toán khoa học", ed. bởi Ramon E. Moore, trang. 99-107, Nhà xuất bản học thuật, Boston, MA

Coonen, Jerome 1984. Đóng góp cho một tiêu chuẩn được đề xuất cho số học dấu phẩy động nhị phân, Luận án tiến sĩ, Đại học. California,Berkeley

Dekker, T. J. 1971. Một kỹ thuật dấu phẩy động để mở rộng độ chính xác có sẵn, số. môn Toán. 18(3), trang. 224-242

Demmel, James 1984. Underflow và Độ tin cậy của Phần mềm Số, SIAM J. Khoa học. thống kê. máy tính. 5(4), trang. 887-919

Farnum, Charles 1988. Hỗ trợ trình biên dịch cho tính toán dấu phẩy động, Thực hành và trải nghiệm phần mềm, 18(7), trang. 701-709

Forsythe, G. e. và Moler, C. b. 1967. Giải pháp máy tính của hệ thống đại số tuyến tính, Prentice-Hall, Englewood Cliffs, NJ

Goldberg, tôi. Bennett 1967. 27 bit không đủ cho độ chính xác 8 chữ số, giao tiếp. của ACM. 10(2), trang 105-106

Goldberg, David 1990. Số học máy tính, trong "Kiến trúc máy tính. Một cách tiếp cận định lượng", bởi David Patterson và John L. Hennessy, Phụ lục A, Morgan Kaufmann, Los Altos, CA

Golub, gen H. và Vân Loan, Charles F. 1989. Tính toán ma trận, tái bản lần thứ 2, Nhà xuất bản Đại học Johns Hopkins, Baltimore Maryland

Graham, Ronald L. , Knuth, Donald E. và Patashnik, Oren. 1989. Toán cụ thể, Addison-Wesley, Reading, MA, p. 162

Hewlett Packard 1982. Sổ tay chức năng nâng cao HP-15C

IEEE 1987. Tiêu chuẩn IEEE 754-1985 cho Số học dấu phẩy động nhị phân, IEEE, (1985). In lại trong SIGPLAN 22(2) trang. 9-25

Kahan, W. 1972. Khảo sát về phân tích lỗi, trong Xử lý thông tin 71, Tập 2, trang. 1214 - 1239 (Ljubljana, Nam Tư), Bắc Hà Lan, Amsterdam

Kahan, W. 1986. Tính diện tích và góc của tam giác hình kim, bản thảo chưa xuất bản

Kahan, W. 1987. Các phép cắt nhánh cho các hàm cơ bản phức tạp, trong "The State of the Art in Numerical Analysis", ed. bởi M. J. D. Powell và A. Iserles (Đại học Birmingham, Anh), Chương 7, Nhà xuất bản Đại học Oxford, New York

Kahan, W. 1988. Các bài giảng chưa được công bố tại Sun Microsystems, Mountain View, CA

Kahan, W. và Coonen, Jerome T. 1982. Tính trực giao gần của cú pháp, ngữ nghĩa và chẩn đoán trong môi trường lập trình số, trong "Mối quan hệ giữa tính toán số và ngôn ngữ lập trình", biên tập. bởi J. k. Reid, trang. 103-115, Bắc Hà Lan, Amsterdam

Kahan, W. và LeBlanc, E. 1985. Sự bất thường trong Gói Acrith của IBM, Proc. Hội nghị chuyên đề IEEE lần thứ 7 về số học máy tính (Urbana, Illinois), trang. 322-331

Kernighan, Brian W. và Ritchie, Dennis M. 1978. Ngôn ngữ lập trình C, Prentice-Hall, Englewood Cliffs, NJ

Kirchner, R. và Kulisch, U. 1987. Số học cho Bộ xử lý Vector, Proc. Hội nghị chuyên đề IEEE lần thứ 8 về số học máy tính (Como, Ý), trang. 256-269

Knuth, Donald E. , 1981. Nghệ thuật lập trình máy tính, Tập II, Phiên bản thứ hai, Addison-Wesley, Reading, MA

Kulisch, U. W. , và Miranker, W. L. 1986. Số học của máy tính kỹ thuật số. Một cách tiếp cận mới, Đánh giá SIAM 28(1), trang 1-36

Matula, D. W. và Kornerup, P. 1985. Số học hợp lý chính xác hữu hạn. Hệ thống số gạch chéo, IEEE Trans. trên may tinh. C-34(1), trang 3-18

Nelson, G. 1991. Lập trình hệ thống với Modula-3, Prentice-Hall, Englewood Cliffs, NJ

Reiser, John F. và Knuth, Donald E. 1975. Evading the Drift in Floating-point Addition, Xử lý thông tin Letters 3(3), trang 84-87

Sterbenz, Miếng vá. 1974. Tính toán dấu chấm động, Prentice-Hall, Englewood Cliffs, NJ

Swartzlander, Bá tước E. và Alexopoulos, Aristides G. 1975. Hệ thống số ký hiệu/logarit, IEEE Trans. máy tính. C-24(12), trang. 1238-1242

Walther, J. S. , 1971. Một thuật toán thống nhất cho các chức năng cơ bản, Kỷ yếu của AFIP Spring Joint Computer Conf. 38, trang. 379-385

Định lý 14 và Định lý 8

Phần này chứa hai trong số các bằng chứng kỹ thuật đã bị bỏ qua trong văn bản

Định lý 14

Cho 0 < k < p và đặt m =
Nhân mảng javascript
k + 1 và giả sử rằng các phép toán dấu phẩy động được làm tròn chính xác. Khi đó (m
Nhân mảng javascript
x)(m
Nhân mảng javascript
xx) chính xác bằng x được làm tròn thành p - k chữ số có nghĩa. Chính xác hơn, x được làm tròn bằng cách lấy ý nghĩa của x, tưởng tượng một điểm cơ số nằm ngay bên trái của k chữ số có nghĩa nhỏ nhất và làm tròn thành một số nguyên.

Bằng chứng

Chứng minh chia thành hai trường hợp, tùy thuộc vào việc tính toán mx =
Nhân mảng javascript
kx + x có thực hiện hay không. Giả sử không có thực hiện. Việc chia tỷ lệ x sao cho nó là một số nguyên là vô hại. Sau đó, tính toán của mx = x +
Nhân mảng javascript
kx trông như thế này.
     n = n/2
144aa. aabb. bb
______2145trong đó x được chia thành hai phần. Các chữ số k bậc thấp được đánh dấu
     n = n/2
132 và các chữ số p - k bậc cao được đánh dấu
     if (n==0) return u
3. Để tính m
Nhân mảng javascript
x từ mx, bạn cần làm tròn k chữ số bậc thấp (những chữ số được đánh dấu bằng
     n = n/2
132) vì vậy(32) m
Nhân mảng javascript
x = mx - x mod . Chính xác hơn(33) r = 1 nếu
     n = n/2
150 làm tròn thành a + 1, r = 0 nếu ngược lại.
Nhân mảng javascript
k) + r
Nhân mảng javascript
k
The value of r is 1 if
     n = n/2
149 is greater thanand 0 otherwise. More precisely(33) r = 1 if
     n = n/2
150 rounds to a + 1, r = 0 otherwise.
Tính tiếp theo m
Nhân mảng javascript
x - x = mx - x mod(
Nhân mảng javascript
k) + r
Nhân mảng javascript
k - x =
Nhân mảng javascript
k(x + r) - x mod(
Nhân mảng javascript
k). The picture below shows the computation of m
Nhân mảng javascript
x - x được làm tròn, tức là (m 
Nhân mảng javascript
x)x. Dòng trên cùng là
Nhân mảng javascript
k(x + r), trong đó
     n = n/2
151 là chữ số có được từ việc thêm
     n = n/2
152 vào chữ số có thứ tự thấp nhất
     n = n/2
132.
     n = n/2
154bb. bb
     n = n/2
155Nếu
     n = n/2
149
Nhân mảng javascript
kx. If
     n = n/2
149 >then r = 1, and 1 is subtracted from
     n = n/2
151 because of the borrow, so the result is
Nhân mảng javascript
kx. Cuối cùng hãy xem xét trường hợp
     n = n/2
149 =. Nếu r = 0 thì
     n = n/2
151 là số chẵn,
     n = n/2
162 là số lẻ và chênh lệch được làm tròn lên, cho kết quả
Nhân mảng javascript
kx. Tương tự khi r = 1,
     n = n/2
151 là số lẻ,
     n = n/2
162 là số chẵn, sự khác biệt được làm tròn xuống, do đó, một lần nữa sự khác biệt là
Nhân mảng javascript
kx. Tóm lại(34) (m
Nhân mảng javascript
x)x =
Nhân mảng javascript
kx
Kết hợp các phương trình và cho (m
Nhân mảng javascript
x) - (m
Nhân mảng javascript
xx) = x - x mod(
Nhân mảng javascript
k) +
Nhân mảng javascript
·
Nhân mảng javascript
k. The result of performing this computation is
     n = n/2
165 bb...bb
______2166Quy tắc tính r, phương trình (33), cũng giống như quy tắc làm tròn
     n = n/2
167
     n = n/2
168 thành p - k. Do đó, tính toán mx - (mx - x) ở độ chính xác số học dấu phẩy động chính xác bằng cách làm tròn x đến p - k vị trí, trong trường hợp khi x +
Nhân mảng javascript
kx không thực hiện. Khi x +
Nhân mảng javascript
kx thực hiện, thì mx =
Nhân mảng javascript
kx + x trông như thế này.
     n = n/2
144aa. aabb. bb
     n = n/2
170Như vậy, m
Nhân mảng javascript
x = mx - x mod(
Nhân mảng javascript
k) + w
Nhân mảng javascript
k, where w = -Z if Z <
Nhân mảng javascript
/2, but the exact value of w is unimportant. Next, m
Nhân mảng javascript
x - x =
Nhân mảng javascript
kx - x mod(
Nhân mảng javascript
k) + w
Nhân mảng javascript
k. In a picture
     n = n/2
171 w
     n = n/2
172Làm tròn cho (m
Nhân mảng javascript
x)x =
Nhân mảng javascript
kx + w
Nhân mảng javascript
k - r
Nhân mảng javascript
k, where r = 1 if
     n = n/2
149 > or if
     n = n/2
149 =and b0 = 1. Finally,(m
Nhân mảng javascript
x) - (m
Nhân mảng javascript
xx) = mx - x mod(
Nhân mảng javascript
k) + w
Nhân mảng javascript
k - (
Nhân mảng javascript
kx + w
Nhân mảng javascript
k - r
Nhân mảng javascript
k)
= x - x mod(
Nhân mảng javascript
k) + r
Nhân mảng javascript
k.
Và một lần nữa, r = 1 chính xác khi làm tròn
     n = n/2
175 thành p - k vị trí liên quan đến việc làm tròn. Như vậy Định lý 14 được chứng minh trong mọi trường hợp. z

Định lý 8 (Công thức tổng Kahan)

Giả sử nó được tính toán bằng thuật toán sau_______02
     n = n/2
08_______04
     n = n/2
5
     n = n/2
6
     n = n/2
7
     n = n/2
8
     n = n/2
9
Sau đó, tổng S được tính toán bằng S =
Nhân mảng javascript
xj (1 +
Nhân mảng javascript
j) + O(N
Nhân mảng javascript
2)
Nhân mảng javascript
|xj|, where |
Nhân mảng javascript
j.
Nhân mảng javascript
2
Nhân mảng javascript
.

Bằng chứng

Đầu tiên hãy nhớ lại ước tính sai số cho công thức đơn giản
Nhân mảng javascript
xi đã diễn ra như thế nào. Giới thiệu s1 = x1, si = (1 +
Nhân mảng javascript
i) (si - 1 + xi). Sau đó, tổng được tính là sn, là tổng của các số hạng, mỗi số là một xi nhân với một biểu thức liên quan đến
Nhân mảng javascript
j's. Hệ số chính xác của x1 là (1 +
Nhân mảng javascript
2)(1 +
Nhân mảng javascript
3). (1 +
Nhân mảng javascript
n), do đó, bằng cách đánh số lại, hệ số của x2 phải là (1 + 
Nhân mảng javascript
3)(1 +
Nhân mảng javascript
4) .. (1 +
Nhân mảng javascript
n), v.v. Việc chứng minh Định lý 8 hoàn toàn giống nhau, chỉ có hệ số của x1 là phức tạp hơn. Cụ thể s0 = c0 = 0 andyk = xkck - 1 = (xk - ck - 1) (1 +
Nhân mảng javascript
k)sk = sk - 1
Nhân mảng javascript
Nhân mảng javascript
yk = . Mặc dù hệ số của x1 trong sk là biểu thức quan tâm cuối cùng, nhưng hóa ra việc tính hệ số của x1 trong sk lại dễ dàng hơn - ck và ck. Khi k = 1,c1 = (s1(1 +
Nhân mảng javascript
k)ck = (sksk - 1)yk= [(sk - sk - 1) (1 +
Nhân mảng javascript
k) - yk] (1 +
Nhân mảng javascript
k)where all the Greek letters are bounded by
Nhân mảng javascript
. Although the coefficient of x1 in sk is the ultimate expression of interest, in turns out to be easier to compute the coefficient of x1 in sk - ck and ck.When k = 1,c1 = (s1(1 +
Nhân mảng javascript
1) - y1) (1 + d1)= y1((1 + s1) (1 +
Nhân mảng javascript
1) - 1) (1 + d1)= x1(s1 +
Nhân mảng javascript
1 + s1g1) (1 + d1) (1 + h1)s1 - c1 = x1[(1 + s1) - (s1 + g1 + s1g1) (1 + d1)](1 + h1)= x1[1 - g1 - s1d1 - s1g1 - d1g1 - s1g1d1](1 + h1)Calling the coefficients of x1 in these expressions Ck and Sk respectively, thenC1 = 2
Nhân mảng javascript
+ O(
Nhân mảng javascript
2)
S1 = +
Nhân mảng javascript
1 -
Nhân mảng javascript
1 + 4
Nhân mảng javascript
2 + O(
Nhân mảng javascript
3)
To get the general formula for Sk and Ck, expand the definitions of sk and ck, ignoring all terms involving xi with i > 1 to getsk = (sk - 1 + yk)(1 +
Nhân mảng javascript
k)= [sk - 1 + (xk - ck - 1) (1 +
Nhân mảng javascript
k)](1 +
Nhân mảng javascript
k)= [(sk - 1 - ck - 1) -
Nhân mảng javascript
kck - 1](1+
Nhân mảng javascript
k)ck = [{sk - sk - 1}(1 +
Nhân mảng javascript
k) - yk](1 +
Nhân mảng javascript
k)= [{((sk - 1 - ck - 1) -
Nhân mảng javascript
kck - 1)(1 +
Nhân mảng javascript
k) - sk - 1}(1 +
Nhân mảng javascript
k) + ck - 1(1 +
Nhân mảng javascript
k)](1 + 
Nhân mảng javascript
k)= [{(sk - 1 - ck - 1)
Nhân mảng javascript
k -
Nhân mảng javascript
kck-1(1 +
Nhân mảng javascript
k) - ck - 1}(1 +
Nhân mảng javascript
k) + ck - 1(1 +
Nhân mảng javascript
k)](1 +
Nhân mảng javascript
k)= [(sk - 1 - ck - 1)
Nhân mảng javascript
k(1 +
Nhân mảng javascript
k) - ck - 1(
Nhân mảng javascript
k +
Nhân mảng javascript
k(
Nhân mảng javascript
k +
Nhân mảng javascript
k +
Nhân mảng javascript
k
Nhân mảng javascript
k))](1 +
Nhân mảng javascript
k),sk - ck = ((sk - 1 - ck - 1) -
Nhân mảng javascript
kck - 1) (1 +
Nhân mảng javascript
k)- [(sk - 1 - ck - 1)
Nhân mảng javascript
k(1 +
Nhân mảng javascript
k) - ck - 1(
Nhân mảng javascript
k +
Nhân mảng javascript
k(
Nhân mảng javascript
k +
Nhân mảng javascript
k +
Nhân mảng javascript
k
Nhân mảng javascript
k)](1 +
Nhân mảng javascript
k)= (sk- 1 - ck - 1)((1 +
Nhân mảng javascript
k) -
Nhân mảng javascript
k(1 +
Nhân mảng javascript
k)(1 +
Nhân mảng javascript
k))+ ck - 1(-
Nhân mảng javascript
k(1 +
Nhân mảng javascript
k) + (
Nhân mảng javascript
k +
Nhân mảng javascript
k(
Nhân mảng javascript
k +
Nhân mảng javascript
k +
Nhân mảng javascript
k
Nhân mảng javascript
k)) (1 +
Nhân mảng javascript
k))= (s- 1 - ck - 1) (1 -
Nhân mảng javascript
k(
Nhân mảng javascript
k +
Nhân mảng javascript
k +
Nhân mảng javascript
k
Nhân mảng javascript
k))+ ck - 1 - [
Nhân mảng javascript
k +
Nhân mảng javascript
k +
Nhân mảng javascript
k(
Nhân mảng javascript
k +
Nhân mảng javascript
k
Nhân mảng javascript
k) + (
Nhân mảng javascript
k +
Nhân mảng javascript
k(
Nhân mảng javascript
k +
Nhân mảng javascript
k +
Nhân mảng javascript
k
Nhân mảng javascript
k))
Nhân mảng javascript
k]Since Sk and Ck are only being computed up to order
Nhân mảng javascript
2, these formulas can be simplified toCk= (
Nhân mảng javascript
k + O(
Nhân mảng javascript
2))Sk - 1 + (-
Nhân mảng javascript
k + O(
Nhân mảng javascript
2))Ck - 1Sk= ((1 + 2
Nhân mảng javascript
2 + O(
Nhân mảng javascript
3))Sk - 1 + (2
Nhân mảng javascript
+
Nhân mảng javascript
(
Nhân mảng javascript
2))Ck - 1Using these formulas givesC2 =
Nhân mảng javascript
2 + O(
Nhân mảng javascript
2)
S2 = 1 +
Nhân mảng javascript
1 -
Nhân mảng javascript
1 + 10
Nhân mảng javascript
2 + O(
Nhân mảng javascript
3)
and in general it is easy to check by induction thatCk =
Nhân mảng javascript
k + O(
Nhân mảng javascript
2)
Sk = 1 +
Nhân mảng javascript
1 -
Nhân mảng javascript
1 + (4k+2)
Nhân mảng javascript
2 + O(
Nhân mảng javascript
3)

Cuối cùng, điều mong muốn là hệ số của x1 trong sk. Để có giá trị này, hãy đặt xn + 1 = 0, đặt tất cả các chữ cái Hy Lạp có chỉ số dưới của n + 1 bằng 0 và tính sn + 1. Khi đó sn + 1 = sn - cn và hệ số của x1 trong sn nhỏ hơn hệ số trong sn + 1, tức là Sn = 1 +

Nhân mảng javascript
1 -
Nhân mảng javascript
1 + (4n + 2)
Nhân mảng javascript
2 = (1 + 2
Nhân mảng javascript
+
Nhân mảng javascript
(n
Nhân mảng javascript
2)). z

Sự khác biệt giữa các triển khai IEEE 754


Lưu ý - Phần này không phải là một phần của bài báo đã xuất bản. Nó đã được thêm vào để làm rõ một số điểm nhất định và sửa chữa những hiểu lầm có thể xảy ra về tiêu chuẩn IEEE mà người đọc có thể suy ra từ bài báo. Tài liệu này không phải do David Goldberg viết, nhưng nó xuất hiện ở đây với sự cho phép của ông

Bài báo trước đã chỉ ra rằng số học dấu phẩy động phải được triển khai cẩn thận, vì các lập trình viên có thể phụ thuộc vào các thuộc tính của nó để đảm bảo tính đúng đắn và chính xác của chương trình của họ. Đặc biệt, tiêu chuẩn IEEE yêu cầu triển khai cẩn thận và chỉ có thể viết các chương trình hữu ích hoạt động chính xác và mang lại kết quả chính xác trên các hệ thống tuân thủ tiêu chuẩn. Người đọc có thể muốn kết luận rằng các chương trình như vậy sẽ có thể di chuyển được đến tất cả các hệ thống của IEEE. Thật vậy, phần mềm di động sẽ dễ viết hơn nếu nhận xét "Khi một chương trình được di chuyển giữa hai máy và cả hai đều hỗ trợ số học IEEE, thì nếu bất kỳ kết quả trung gian nào khác đi, thì đó phải là do lỗi phần mềm, không phải do sự khác biệt về số học," là

Thật không may, tiêu chuẩn IEEE không đảm bảo rằng cùng một chương trình sẽ mang lại kết quả giống hệt nhau trên tất cả các hệ thống phù hợp. Hầu hết các chương trình sẽ thực sự tạo ra các kết quả khác nhau trên các hệ thống khác nhau vì nhiều lý do. Đầu tiên, hầu hết các chương trình liên quan đến việc chuyển đổi số giữa định dạng thập phân và nhị phân và tiêu chuẩn IEEE không chỉ định đầy đủ độ chính xác mà các chuyển đổi đó phải được thực hiện. Mặt khác, nhiều chương trình sử dụng các chức năng cơ bản do thư viện hệ thống cung cấp và tiêu chuẩn hoàn toàn không chỉ định các chức năng này. Tất nhiên, hầu hết các lập trình viên đều biết rằng các tính năng này nằm ngoài phạm vi của tiêu chuẩn IEEE.

Nhiều lập trình viên có thể không nhận ra rằng ngay cả một chương trình chỉ sử dụng các định dạng số và hoạt động theo tiêu chuẩn IEEE có thể tính toán các kết quả khác nhau trên các hệ thống khác nhau. Trên thực tế, các tác giả của tiêu chuẩn dự định cho phép các triển khai khác nhau thu được các kết quả khác nhau. Ý định của họ thể hiện rõ trong định nghĩa về thuật ngữ đích trong tiêu chuẩn IEEE 754. "Đích có thể được chỉ định rõ ràng bởi người dùng hoặc được cung cấp ngầm bởi hệ thống (ví dụ: kết quả trung gian trong các biểu thức con hoặc đối số cho các thủ tục). Một số ngôn ngữ đặt kết quả tính toán trung gian ở đích ngoài tầm kiểm soát của người dùng. Tuy nhiên, tiêu chuẩn này xác định kết quả của một hoạt động theo định dạng của đích đó và các giá trị của toán hạng. " (IEEE 754-1985, tr. 7) Nói cách khác, tiêu chuẩn IEEE yêu cầu mỗi kết quả phải được làm tròn chính xác theo độ chính xác của đích mà nó sẽ được đặt vào, nhưng tiêu chuẩn không yêu cầu độ chính xác của đích đó được xác định bởi chương trình của người dùng. Do đó, các hệ thống khác nhau có thể đưa kết quả của chúng đến các đích với độ chính xác khác nhau, khiến cùng một chương trình tạo ra các kết quả khác nhau (đôi khi rất đáng kể), mặc dù các hệ thống đó đều tuân theo tiêu chuẩn

Một số ví dụ trong bài báo trước phụ thuộc vào một số kiến ​​thức về cách làm tròn số học dấu phẩy động. Để dựa vào các ví dụ như thế này, một lập trình viên phải có khả năng dự đoán cách một chương trình sẽ được diễn giải và đặc biệt, trên hệ thống IEEE, độ chính xác của đích đến của mỗi phép toán số học có thể là bao nhiêu. Than ôi, lỗ hổng trong định nghĩa đích của tiêu chuẩn IEEE làm suy yếu khả năng của lập trình viên để biết chương trình sẽ được diễn giải như thế nào. Do đó, một số ví dụ nêu trên, khi được triển khai dưới dạng các chương trình di động rõ ràng bằng ngôn ngữ cấp cao, có thể không hoạt động chính xác trên các hệ thống IEEE thường cung cấp kết quả tới đích với độ chính xác khác với mong đợi của lập trình viên. Các ví dụ khác có thể hoạt động, nhưng việc chứng minh rằng chúng hoạt động có thể nằm ngoài khả năng của một lập trình viên bình thường

Trong phần này, chúng tôi phân loại các triển khai số học IEEE 754 hiện có dựa trên độ chính xác của các định dạng đích mà chúng thường sử dụng. Sau đó, chúng tôi xem xét một số ví dụ từ bài báo để chỉ ra rằng việc cung cấp kết quả với độ chính xác rộng hơn mong đợi của chương trình có thể khiến chương trình tính toán kết quả sai mặc dù nó có thể đúng khi sử dụng độ chính xác dự kiến. Chúng tôi cũng xem lại một trong những bằng chứng trong bài báo để minh họa nỗ lực trí tuệ cần thiết để đối phó với độ chính xác ngoài dự kiến ​​ngay cả khi nó không làm mất hiệu lực chương trình của chúng tôi. Những ví dụ này cho thấy rằng bất chấp tất cả những gì tiêu chuẩn IEEE quy định, sự khác biệt mà tiêu chuẩn này cho phép giữa các triển khai khác nhau có thể ngăn chúng ta viết phần mềm số hiệu quả, di động có hành vi mà chúng ta có thể dự đoán chính xác. Sau đó, để phát triển phần mềm như vậy, trước tiên chúng ta phải tạo ra các ngôn ngữ lập trình và môi trường hạn chế tính biến đổi mà tiêu chuẩn IEEE cho phép và cho phép các lập trình viên thể hiện ngữ nghĩa dấu phẩy động mà chương trình của họ phụ thuộc vào.

Triển khai IEEE 754 hiện tại

Việc triển khai số học IEEE 754 hiện tại có thể được chia thành hai nhóm được phân biệt theo mức độ chúng hỗ trợ các định dạng dấu phẩy động khác nhau trong phần cứng. Các hệ thống dựa trên mở rộng, được minh họa bởi dòng bộ xử lý Intel x86, cung cấp hỗ trợ đầy đủ cho định dạng độ chính xác kép mở rộng nhưng chỉ hỗ trợ một phần cho độ chính xác đơn và kép. chúng cung cấp các hướng dẫn để tải hoặc lưu trữ dữ liệu ở độ chính xác đơn và kép, chuyển đổi dữ liệu nhanh chóng sang hoặc từ định dạng kép mở rộng và chúng cung cấp các chế độ đặc biệt (không phải mặc định) trong đó kết quả của các phép toán số học được làm tròn thành đơn . (Bộ xử lý sê-ri Motorola 68000 làm tròn kết quả cho cả độ chính xác và phạm vi của định dạng đơn hoặc kép trong các chế độ này. Intel x86 và bộ xử lý tương thích làm tròn kết quả thành độ chính xác của định dạng đơn hoặc kép nhưng vẫn giữ nguyên phạm vi như định dạng kép mở rộng. ) Các hệ thống đơn/kép, bao gồm hầu hết các bộ xử lý RISC, cung cấp hỗ trợ đầy đủ cho các định dạng chính xác đơn và kép nhưng không hỗ trợ định dạng chính xác kép mở rộng tuân thủ IEEE. (Kiến trúc IBM POWER chỉ cung cấp hỗ trợ một phần cho độ chính xác đơn, nhưng với mục đích của phần này, chúng tôi phân loại nó là hệ thống đơn/kép. )

Để xem cách tính toán có thể hoạt động khác nhau trên hệ thống dựa trên mở rộng so với trên hệ thống đơn/kép, hãy xem xét phiên bản C của ví dụ từ phần

     n = n/2
30
     n = n/2
31
     n = n/2
32
     n = n/2
33
     n = n/2
34
     n = n/2
35
     n = n/2
9

Đây là hằng số 3. 0 và 7. 0 được hiểu là số dấu phẩy động có độ chính xác kép và biểu thức 3. 0/7. 0 kế thừa kiểu dữ liệu

     n = n/2
176. Trên một hệ thống đơn/kép, biểu thức sẽ được đánh giá với độ chính xác kép vì đó là định dạng hiệu quả nhất để sử dụng. Do đó,
     n = n/2
053 sẽ được gán giá trị 3. 0/7. 0 được làm tròn chính xác để tăng gấp đôi độ chính xác. Trong dòng tiếp theo, biểu thức 3. 0/7. 0 một lần nữa sẽ được đánh giá với độ chính xác kép và tất nhiên kết quả sẽ bằng với giá trị vừa được gán cho
     n = n/2
053, vì vậy chương trình sẽ in "Bằng" như mong đợi

Trên một hệ thống dựa trên mở rộng, mặc dù biểu thức 3. 0/7. 0 có loại

     n = n/2
176, thương số sẽ được tính trong một thanh ghi ở định dạng kép mở rộng và do đó ở chế độ mặc định, nó sẽ được làm tròn thành độ chính xác kép mở rộng. Tuy nhiên, khi giá trị kết quả được gán cho biến
     n = n/2
053, thì nó có thể được lưu trữ trong bộ nhớ và vì
     n = n/2
053 được khai báo là
     n = n/2
176, nên giá trị sẽ được làm tròn thành độ chính xác gấp đôi. Trong dòng tiếp theo, biểu thức 3. 0/7. 0 một lần nữa có thể được đánh giá ở độ chính xác mở rộng mang lại kết quả khác với giá trị độ chính xác kép được lưu trữ trong
     n = n/2
053, khiến chương trình in ra "Không bằng". Tất nhiên, các kết quả khác cũng có thể xảy ra. trình biên dịch có thể quyết định lưu trữ và do đó làm tròn giá trị của biểu thức 3. 0/7. 0 ở dòng thứ hai trước khi so sánh nó với
     n = n/2
053 hoặc nó có thể giữ
     n = n/2
053 trong sổ đăng ký với độ chính xác mở rộng mà không cần lưu trữ. Trình biên dịch tối ưu hóa có thể đánh giá biểu thức 3. 0/7. 0 tại thời điểm biên dịch, có lẽ ở độ chính xác kép hoặc có thể ở độ chính xác kép mở rộng. (Với một trình biên dịch x86, chương trình sẽ in "Bằng" khi được biên dịch với tối ưu hóa và "Không bằng" khi được biên dịch để gỡ lỗi. ) Cuối cùng, một số trình biên dịch cho các hệ thống dựa trên mở rộng tự động thay đổi chế độ làm tròn độ chính xác để khiến các hoạt động tạo ra kết quả trong các thanh ghi để làm tròn các kết quả đó thành độ chính xác đơn hoặc kép, mặc dù có thể với phạm vi rộng hơn. Do đó, trên các hệ thống này, chúng tôi không thể dự đoán hành vi của chương trình chỉ bằng cách đọc mã nguồn của nó và áp dụng hiểu biết cơ bản về số học IEEE 754. Chúng tôi cũng không thể buộc tội phần cứng hoặc trình biên dịch không cung cấp môi trường tuân thủ IEEE 754;

Cạm bẫy trong tính toán trên các hệ thống dựa trên mở rộng

Sự khôn ngoan thông thường cho rằng các hệ thống dựa trên mở rộng phải tạo ra kết quả ít nhất là chính xác, nếu không muốn nói là chính xác hơn kết quả được cung cấp trên hệ thống đơn/kép, vì hệ thống trước luôn cung cấp độ chính xác ít nhất bằng và thường cao hơn hệ thống sau. Các ví dụ tầm thường như chương trình C ở trên cũng như các chương trình phức tạp hơn dựa trên các ví dụ được thảo luận bên dưới cho thấy sự khôn ngoan này tốt nhất là ngây thơ. một số chương trình rõ ràng là di động, thực sự là di động trên các hệ thống đơn/kép, cung cấp kết quả không chính xác trên các hệ thống dựa trên mở rộng chính xác do trình biên dịch và phần cứng đôi khi cung cấp độ chính xác cao hơn mong đợi của chương trình

Các ngôn ngữ lập trình hiện tại khiến chương trình khó xác định độ chính xác mà nó mong đợi. Như phần này đã đề cập, nhiều ngôn ngữ lập trình không chỉ định rằng mỗi lần xuất hiện của một biểu thức như

     n = n/2
035 trong cùng một ngữ cảnh sẽ đánh giá cùng một giá trị. Một số ngôn ngữ, chẳng hạn như Ada, bị ảnh hưởng về mặt này bởi các biến thể giữa các số học khác nhau trước khi có tiêu chuẩn IEEE. Gần đây hơn, các ngôn ngữ như ANSI C đã bị ảnh hưởng bởi các hệ thống dựa trên mở rộng phù hợp với tiêu chuẩn. Trên thực tế, tiêu chuẩn ANSI C rõ ràng cho phép trình biên dịch đánh giá một biểu thức dấu phẩy động với độ chính xác rộng hơn mức thường được kết hợp với loại của nó. Do đó, giá trị của biểu thức
     n = n/2
035 có thể khác nhau tùy thuộc vào nhiều yếu tố. liệu biểu thức được gán ngay cho một biến hay xuất hiện dưới dạng biểu thức con trong biểu thức lớn hơn;

Các tiêu chuẩn ngôn ngữ không hoàn toàn đổ lỗi cho sự thất thường của việc đánh giá biểu thức. Các hệ thống dựa trên mở rộng chạy hiệu quả nhất khi các biểu thức được đánh giá trong các thanh ghi độ chính xác mở rộng bất cứ khi nào có thể, tuy nhiên các giá trị phải được lưu trữ được lưu trữ ở độ chính xác hẹp nhất được yêu cầu. Hạn chế một ngôn ngữ để yêu cầu đánh giá

     n = n/2
035 với cùng một giá trị ở mọi nơi sẽ áp dụng hình phạt hiệu suất đối với các hệ thống đó. Thật không may, việc cho phép các hệ thống đó đánh giá
     n = n/2
035 khác nhau trong các ngữ cảnh tương đương về mặt cú pháp sẽ tự áp đặt hình phạt đối với các lập trình viên của phần mềm số chính xác bằng cách ngăn họ dựa vào cú pháp của chương trình để thể hiện ngữ nghĩa dự định của họ

Các chương trình thực tế có phụ thuộc vào giả định rằng một biểu thức đã cho luôn đánh giá cùng một giá trị không?

_______537____538____539____540
     n = n/2
41
     n = n/2
42____543
     n = n/2
44

Trên hệ thống dựa trên mở rộng, trình biên dịch có thể đánh giá biểu thức

     n = n/2
190 
     n = n/2
044 
     n = n/2
006 ở dòng thứ ba với độ chính xác mở rộng và so sánh kết quả với
     n = n/2
190. Tuy nhiên, khi biểu thức tương tự được chuyển đến hàm nhật ký ở dòng thứ sáu, trình biên dịch có thể lưu trữ giá trị của nó trong bộ nhớ, làm tròn nó thành độ chính xác đơn. Do đó, nếu
     n = n/2
006 không nhỏ đến mức
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 làm tròn thành
     n = n/2
190 với độ chính xác mở rộng nhưng đủ nhỏ để
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 làm tròn thành
     n = n/2
190 với độ chính xác đơn, thì giá trị trả về bởi
     n = n/2
03 sẽ bằng 0 thay vì
     n = n/2
006 và lỗi tương đối sẽ . Tương tự, giả sử phần còn lại của biểu thức ở dòng thứ sáu, bao gồm cả sự xuất hiện lại của biểu thức con
     n = n/2
190 
     n = n/2
044 
     n = n/2
006, được đánh giá theo độ chính xác mở rộng. Trong trường hợp đó, nếu
     n = n/2
006 nhỏ nhưng không đủ nhỏ để
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 làm tròn thành
     n = n/2
190 với độ chính xác duy nhất, thì giá trị do
     n = n/2
03 trả về có thể vượt quá giá trị chính xác gần bằng
     n = n/2
006 và một lần nữa, lỗi tương đối có thể đạt tới một. Ví dụ cụ thể, lấy
     n = n/2
006 là 2-24 + 2-47, vì vậy,
     n = n/2
006 là số chính xác đơn lẻ nhỏ nhất sao cho
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 làm tròn thành số lớn hơn tiếp theo, 1 + 2-23. Khi đó
     n = n/2
20 
     n = n/2
044 
     n = n/2
22 xấp xỉ 2-23. Bởi vì mẫu số trong biểu thức ở dòng thứ sáu được đánh giá ở độ chính xác mở rộng, nó được tính toán chính xác và mang lại giá trị
     n = n/2
006, do đó,
     n = n/2
03 trả về khoảng 2-23, lớn gần gấp đôi giá trị chính xác. (Điều này thực sự xảy ra với ít nhất một trình biên dịch. Khi mã trước đó được biên dịch bởi Sun WorkShop Compilers 4. 2. 1 Trình biên dịch Fortran 77 dành cho các hệ thống x86 sử dụng cờ tối ưu hóa
     n = n/2
25, mã được tạo sẽ tính toán
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 chính xác như mô tả. Kết quả là, hàm trả về 0 cho
     n = n/2
29 và
     n = n/2
30 cho
     n = n/2
31. )
Nhân mảng javascript
. Similarly, suppose the rest of the expression in the sixth line, including the reoccurrence of the subexpression
     n = n/2
190 
     n = n/2
044 
     n = n/2
006, is evaluated in extended precision. In that case, if
     n = n/2
006 is small but not quite small enough that
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 rounds to
     n = n/2
190 in single precision, then the value returned by
     n = n/2
03 can exceed the correct value by nearly as much as
     n = n/2
006, and again the relative error can approach one. For a concrete example, take
     n = n/2
006 to be 2-24 + 2-47, so
     n = n/2
006 is the smallest single precision number such that
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 rounds up to the next larger number, 1 + 2-23. Then
     n = n/2
20 
     n = n/2
044 
     n = n/2
22 is approximately 2-23. Because the denominator in the expression in the sixth line is evaluated in extended precision, it is computed exactly and delivers
     n = n/2
006, so
     n = n/2
03 returns approximately 2-23, which is nearly twice as large as the exact value. (This actually happens with at least one compiler. When the preceding code is compiled by the Sun WorkShop Compilers 4.2.1 Fortran 77 compiler for x86 systems using the
     n = n/2
25 optimization flag, the generated code computes
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 exactly as described. As a result, the function delivers zero for
     n = n/2
29 and
     n = n/2
30 for
     n = n/2
31.)

Để thuật toán của Định lý 4 hoạt động chính xác, biểu thức

     n = n/2
190 
     n = n/2
044 
     n = n/2
006 phải được đánh giá theo cùng một cách mỗi khi nó xuất hiện; . Tất nhiên, vì
     n = n/2
38 là một hàm nội tại chung trong Fortran, trình biên dịch có thể đánh giá biểu thức
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 với độ chính xác mở rộng xuyên suốt, tính logarit của nó với cùng độ chính xác, nhưng rõ ràng chúng ta không thể cho rằng trình biên dịch sẽ làm như vậy. (Người ta cũng có thể tưởng tượng một ví dụ tương tự liên quan đến hàm do người dùng định nghĩa. Trong trường hợp đó, một trình biên dịch vẫn có thể giữ đối số ở độ chính xác mở rộng mặc dù hàm trả về một kết quả chính xác duy nhất, nhưng rất ít nếu có bất kỳ trình biên dịch Fortran hiện có nào làm được điều này. ) Do đó, chúng tôi có thể cố gắng đảm bảo rằng
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 được đánh giá một cách nhất quán bằng cách gán nó cho một biến. Thật không may, nếu chúng ta khai báo biến
     n = n/2
45 đó, thì chúng ta vẫn có thể bị trình biên dịch làm hỏng khi thay thế một giá trị được lưu trong sổ đăng ký với độ chính xác mở rộng cho một lần xuất hiện của biến và một giá trị được lưu trong bộ nhớ với độ chính xác duy nhất cho một lần xuất hiện khác. Thay vào đó, chúng ta cần khai báo biến có kiểu tương ứng với định dạng chính xác mở rộng. FORTRAN 77 tiêu chuẩn không cung cấp cách để thực hiện việc này và trong khi Fortran 95 cung cấp cơ chế
     n = n/2
46 để mô tả các định dạng khác nhau, nó không yêu cầu rõ ràng việc triển khai đánh giá các biểu thức với độ chính xác mở rộng để cho phép các biến được khai báo với độ chính xác đó. Nói tóm lại, không có cách di động nào để viết chương trình này bằng Fortran tiêu chuẩn được đảm bảo để ngăn biểu thức
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 bị đánh giá theo cách làm mất hiệu lực bằng chứng của chúng tôi

Có những ví dụ khác có thể gặp trục trặc trên các hệ thống dựa trên mở rộng ngay cả khi mỗi biểu thức con được lưu trữ và do đó được làm tròn với cùng độ chính xác. Nguyên nhân là do làm tròn hai lần. Ở chế độ chính xác mặc định, một hệ thống dựa trên mở rộng ban đầu sẽ làm tròn từng kết quả thành độ chính xác kép mở rộng. Nếu kết quả đó sau đó được lưu trữ với độ chính xác gấp đôi, nó sẽ được làm tròn lại. Sự kết hợp của hai cách làm tròn này có thể mang lại một giá trị khác với giá trị có được bằng cách làm tròn kết quả đầu tiên một cách chính xác để tăng gấp đôi độ chính xác. Điều này có thể xảy ra khi kết quả được làm tròn thành độ chính xác kép mở rộng là "trường hợp giữa chừng", tôi. e. , nó nằm chính xác ở giữa hai số chính xác kép, do đó, lần làm tròn thứ hai được xác định theo quy tắc làm tròn số chẵn. Nếu lần làm tròn thứ hai này quay cùng hướng với lần đầu tiên, thì sai số làm tròn ròng sẽ vượt quá nửa đơn vị ở vị trí cuối cùng. (Tuy nhiên, xin lưu ý rằng việc làm tròn hai lần chỉ ảnh hưởng đến các phép tính có độ chính xác kép. Người ta có thể chứng minh rằng tổng, hiệu, tích hoặc thương của hai số p-bit hoặc căn bậc hai của một số p-bit, trước tiên được làm tròn thành q bit và sau đó thành p bit cho cùng một giá trị như thể kết quả là . Do đó, độ chính xác kép mở rộng đủ rộng để các tính toán chính xác đơn lẻ không bị làm tròn kép. )

Nhân mảng javascript
2p + 2. Thus, extended double precision is wide enough that single precision computations don't suffer double-rounding.)

Một số thuật toán phụ thuộc vào làm tròn chính xác có thể thất bại với làm tròn kép. Trên thực tế, ngay cả một số thuật toán không yêu cầu làm tròn chính xác và hoạt động chính xác trên nhiều loại máy không tuân thủ IEEE 754 cũng có thể thất bại khi làm tròn hai lần. Hữu ích nhất trong số này là các thuật toán di động để thực hiện nhiều phép tính chính xác mô phỏng được đề cập trong phần. Ví dụ: quy trình được mô tả trong Định lý 6 để tách một số dấu phẩy động thành các phần cao và thấp không hoạt động chính xác trong số học làm tròn kép. cố gắng chia số chính xác kép 252 + 3 × 226 - 1 thành hai phần, mỗi phần có tối đa 26 bit. Khi mỗi thao tác được làm tròn chính xác thành độ chính xác gấp đôi, phần có thứ tự cao là 252 + 227 và phần có thứ tự thấp là 226 - 1, nhưng khi mỗi thao tác được làm tròn trước thành độ chính xác gấp đôi mở rộng rồi đến độ chính xác gấp đôi, quy trình sẽ tạo ra giá trị cao . Số thứ hai chiếm 27 bit, vì vậy bình phương của nó không thể được tính chính xác với độ chính xác gấp đôi. Tất nhiên, vẫn có thể tính bình phương của số này với độ chính xác gấp đôi mở rộng, nhưng thuật toán kết quả sẽ không còn khả dụng cho các hệ thống đơn/kép. Ngoài ra, các bước sau trong thuật toán nhân chính xác bội giả định rằng tất cả các tích từng phần đã được tính toán với độ chính xác kép. Xử lý chính xác hỗn hợp các biến kép kép và mở rộng sẽ khiến việc triển khai trở nên đắt đỏ hơn đáng kể

Tương tự như vậy, các thuật toán di động để thêm nhiều số chính xác được biểu thị dưới dạng mảng các số chính xác kép có thể không thành công trong phép tính làm tròn hai lần. Các thuật toán này thường dựa trên một kỹ thuật tương tự như công thức tính tổng của Kahan. Như lời giải thích không chính thức của công thức tính tổng được đưa ra trên gợi ý, nếu

     n = n/2
50 và
     n = n/2
042 là các biến dấu phẩy động với.
     n = n/2
50.
Nhân mảng javascript
.
     n = n/2
042. và chúng tôi tính toán.

________ 545 ________ 546

sau đó trong hầu hết các phép tính, ________ 054 khôi phục chính xác lỗi làm tròn xảy ra trong máy tính ________ 055. Tuy nhiên, kỹ thuật này không hoạt động trong số học làm tròn hai lần. nếu

     n = n/2
50 = 252 + 1 và
     n = n/2
042 = 1/2 - 2-54, thì
     n = n/2
50 
     n = n/2
044 
     n = n/2
042 làm tròn đầu tiên thành 252 + 3/2 với độ chính xác kép mở rộng và giá trị này làm tròn thành 252 + 2 với độ chính xác kép theo các mối quan hệ . Một lần nữa, ở đây, có thể khôi phục lỗi làm tròn bằng cách tính tổng ở độ chính xác kép mở rộng, nhưng sau đó chương trình sẽ phải thực hiện thêm công việc để giảm kết quả cuối cùng trở lại độ chính xác gấp đôi và việc làm tròn kép có thể ảnh hưởng đến quá trình này, . Vì lý do này, mặc dù các chương trình di động để mô phỏng nhiều phép tính chính xác bằng các phương pháp này hoạt động chính xác và hiệu quả trên nhiều loại máy, nhưng chúng không hoạt động như quảng cáo trên các hệ thống dựa trên mở rộng

Cuối cùng, một số thuật toán thoạt nhìn có vẻ phụ thuộc vào việc làm tròn chính xác trên thực tế có thể hoạt động chính xác với phép làm tròn hai lần. Trong những trường hợp này, chi phí đối phó với việc làm tròn hai lần không nằm ở việc triển khai mà nằm ở việc xác minh rằng thuật toán có hoạt động như quảng cáo hay không. Để minh họa, ta chứng minh biến thể sau của Định lý 7

Định lý 7'

Nếu m và n là các số nguyên có thể biểu diễn theo độ chính xác kép của IEEE 754 với. m. < 252 và n có dạng đặc biệt n = 2i + 2j, sau đó (mn)
Nhân mảng javascript
n = m, miễn là cả hai phép toán dấu phẩy động đều được làm tròn chính xác thành gấp đôi độ chính xác hoặc làm tròn trước thành gấp đôi mở rộng .

Bằng chứng

Giả sử không mất mát rằng m > 0. Đặt q = mn. Chia tỷ lệ theo lũy thừa hai, chúng ta có thể xem xét một cài đặt tương đương trong đó 252

Nhân mảng javascript
m < 253 và tương tự như vậy đối với q, sao cho cả m và q đều là số nguyên có bit ít quan trọng nhất chiếm vị trí đơn vị (i. e. , ulp(m) = ulp(q) = 1). Trước khi chia tỷ lệ, chúng tôi giả sử m < 252, vì vậy sau khi chia tỷ lệ, m là một số nguyên chẵn. Ngoài ra, vì các giá trị tỷ lệ của m và q thỏa mãn m/2 < q < 2m nên giá trị tương ứng của n phải có một trong hai dạng tùy thuộc vào m hay q lớn hơn. nếu q 

Gọi e là lỗi làm tròn trong phép tính q, sao cho q = m/n + e và giá trị được tính q

Nhân mảng javascript
n sẽ là giá trị được làm tròn (một hoặc hai lần) . Trước tiên, hãy xem xét trường hợp trong đó mỗi thao tác dấu phẩy động được làm tròn chính xác để tăng gấp đôi độ chính xác. Trong trường hợp này,. e. < 1/2. Nếu n có dạng 1/2 + 2-(k + 1) thì ne = nq - m là bội số nguyên của 2-(k + 1) và. ne. < 1/4 + 2-(k + 2). Điều này ngụ ý rằng. ne.
Nhân mảng javascript
1/4. Nhớ lại rằng hiệu giữa m và số có thể biểu thị lớn hơn tiếp theo là 1 và hiệu giữa m và số có thể biểu thị nhỏ hơn tiếp theo là 1 nếu m > 252 hoặc 1/2 nếu m = 252. Vì vậy, như. ne.
Nhân mảng javascript
1/4, m + ne sẽ làm tròn thành m. (Ngay cả khi m = 252 và ne = -1/4, tích sẽ làm tròn thành m theo quy tắc làm tròn số chẵn. ) Tương tự, nếu n có dạng 1 + 2-k, thì ne là bội số nguyên của 2-k và. ne.
Nhân mảng javascript
1/2. Chúng ta không thể có m = 252 trong trường hợp này vì m hoàn toàn lớn hơn q, vì vậy m khác với các lân cận có thể biểu diễn gần nhất của nó ±1. Vì vậy, như. ne.
Nhân mảng javascript
1/2, lại m + ne sẽ làm tròn thành m. (Thậm chí nếu. ne. = 1/2, tích sẽ làm tròn thành m theo quy tắc làm tròn số chẵn vì m chẵn. ) Điều này hoàn thành bằng chứng cho số học được làm tròn chính xác.

Trong số học làm tròn hai lần, vẫn có thể xảy ra trường hợp q là thương được làm tròn chính xác (mặc dù nó thực sự đã được làm tròn hai lần), vì vậy. e. < 1/2 như trên. Trong trường hợp này, chúng tôi có thể kháng cáo các lập luận của đoạn trước miễn là chúng tôi xem xét thực tế rằng q

Nhân mảng javascript
n sẽ được làm tròn hai lần. Để giải thích điều này, hãy lưu ý rằng tiêu chuẩn IEEE yêu cầu định dạng kép mở rộng mang ít nhất 64 bit có nghĩa, sao cho các số m ± 1/2 và m ± 1/4 có thể biểu diễn chính xác ở độ chính xác kép mở rộng. Do đó, nếu n có dạng 1/2 + 2-(k + 1), sao cho. ne.
Nhân mảng javascript
1/4, sau đó làm tròn m + ne thành độ chính xác kép mở rộng phải tạo ra kết quả khác với m nhiều nhất là 1/4 và như đã lưu ý ở trên, giá trị này sẽ làm tròn thành m gấp đôi . Tương tự, nếu n có dạng 1 + 2-k, sao cho. ne.
Nhân mảng javascript
1/2, sau đó làm tròn m + ne thành độ chính xác kép mở rộng phải tạo ra kết quả khác với m nhiều nhất là 1/2 và giá trị này sẽ làm tròn thành m ở độ chính xác kép. (Nhắc lại m > 252 trong trường hợp này. )

Cuối cùng, chúng ta còn lại để xem xét các trường hợp trong đó q không phải là thương được làm tròn chính xác do làm tròn hai lần. Trong những trường hợp này, chúng ta có. e. < 1/2 + 2-(d + 1) trong trường hợp xấu nhất, trong đó d là số bit thừa ở định dạng kép mở rộng. (Tất cả các hệ thống dựa trên mở rộng hiện có đều hỗ trợ định dạng kép mở rộng với chính xác 64 bit có nghĩa; đối với định dạng này, d = 64 - 53 = 11. ) Vì làm tròn hai lần chỉ tạo ra kết quả làm tròn không chính xác khi làm tròn thứ hai được xác định theo quy tắc làm tròn về số chẵn, nên q phải là một số nguyên chẵn. Do đó, nếu n có dạng 1/2 + 2-(k + 1), thì ne = nq - m là bội số nguyên của 2-k và

ne. < (1/2 + 2-(k + 1))(1/2 + 2-(d + 1)) = 1/4 + 2-(k + 2) + 2-(d + 2) + 2-

Nếu k

Nhân mảng javascript
d, điều này ngụ ý. ne.
Nhân mảng javascript
1/4. Nếu k > d, ta có. ne.
Nhân mảng javascript
1/4 + 2-(d + 2). Trong cả hai trường hợp, lần làm tròn đầu tiên của tích sẽ mang lại kết quả khác với m nhiều nhất là 1/4 và theo các đối số trước đó, lần làm tròn thứ hai sẽ làm tròn thành m. Tương tự, nếu n có dạng 1 + 2-k thì ne là bội số nguyên của 2-(k - 1) và

ne. < 1/2 + 2-(k + 1) + 2-(d + 1) + 2-(k + d + 1)

Nếu k

Nhân mảng javascript
d, điều này ngụ ý. ne.
Nhân mảng javascript
1/2. Nếu k > d, ta có. ne.
Nhân mảng javascript
1/2 + 2-(d + 1). Trong cả hai trường hợp, lần làm tròn đầu tiên của tích sẽ mang lại kết quả khác với m nhiều nhất là 1/2 và một lần nữa theo các đối số trước đó, lần làm tròn thứ hai sẽ làm tròn thành m. z

Bằng chứng trước cho thấy tích chỉ có thể làm tròn hai lần nếu thương thực hiện, và thậm chí sau đó, nó làm tròn thành kết quả đúng. Bằng chứng cũng chỉ ra rằng việc mở rộng lập luận của chúng ta để bao gồm khả năng làm tròn hai lần có thể là một thách thức ngay cả đối với một chương trình chỉ có hai phép tính dấu phẩy động. Đối với một chương trình phức tạp hơn, có thể không thể tính toán một cách có hệ thống các tác động của phép làm tròn hai lần, chưa kể đến các kết hợp tổng quát hơn của phép tính chính xác kép mở rộng và kép

Hỗ trợ ngôn ngữ lập trình cho độ chính xác mở rộng

Không nên dùng các ví dụ trước để gợi ý rằng bản thân độ chính xác mở rộng là có hại. Nhiều chương trình có thể được hưởng lợi từ độ chính xác mở rộng khi lập trình viên có thể sử dụng nó một cách có chọn lọc. Thật không may, các ngôn ngữ lập trình hiện tại không cung cấp đủ phương tiện để lập trình viên chỉ định thời điểm và cách sử dụng độ chính xác mở rộng. Để chỉ ra những hỗ trợ nào là cần thiết, chúng tôi xem xét các cách mà chúng tôi có thể muốn quản lý việc sử dụng độ chính xác mở rộng

Trong một chương trình di động sử dụng độ chính xác kép làm độ chính xác làm việc danh nghĩa của nó, có năm cách chúng ta có thể muốn kiểm soát việc sử dụng độ chính xác rộng hơn

  1. Biên dịch để tạo mã nhanh nhất, sử dụng độ chính xác mở rộng nếu có thể trên các hệ thống dựa trên mở rộng. Rõ ràng hầu hết các phần mềm số không yêu cầu nhiều phép tính hơn là lỗi tương đối trong mỗi thao tác được giới hạn bởi "máy epsilon". Khi dữ liệu trong bộ nhớ được lưu trữ ở độ chính xác kép, epsilon của máy thường được coi là lỗi làm tròn tương đối lớn nhất ở độ chính xác đó, vì dữ liệu đầu vào (đúng hoặc sai) được giả định là đã được làm tròn khi chúng được nhập và kết quả sẽ . Do đó, trong khi tính toán một số kết quả trung gian ở độ chính xác mở rộng có thể mang lại kết quả chính xác hơn, thì độ chính xác mở rộng là không cần thiết. Trong trường hợp này, chúng tôi có thể muốn trình biên dịch chỉ sử dụng độ chính xác mở rộng khi nó sẽ không làm chậm chương trình một cách đáng kể và nếu không thì sử dụng độ chính xác kép
  2. Sử dụng định dạng rộng hơn gấp đôi nếu định dạng đó đủ nhanh và đủ rộng, nếu không thì sử dụng định dạng khác. Một số tính toán có thể được thực hiện dễ dàng hơn khi có độ chính xác mở rộng, nhưng chúng cũng có thể được thực hiện với độ chính xác kép chỉ với nỗ lực lớn hơn một chút. Xem xét tính toán định mức Euclide của một vectơ số chính xác kép. Bằng cách tính bình phương của các phần tử và tích lũy tổng của chúng ở định dạng kép mở rộng IEEE 754 với phạm vi số mũ rộng hơn, chúng ta có thể tránh được tình trạng tràn hoặc tràn sớm đối với các vectơ có độ dài thực tế một cách tầm thường. Trên các hệ thống dựa trên mở rộng, đây là cách nhanh nhất để tính định mức. Trên các hệ thống đơn/kép, một định dạng kép mở rộng sẽ phải được mô phỏng trong phần mềm (nếu một phần mềm được hỗ trợ) và việc mô phỏng như vậy sẽ chậm hơn nhiều so với việc chỉ sử dụng độ chính xác kép, kiểm tra các cờ ngoại lệ để xác định xem có xảy ra tình trạng tràn hoặc tràn . Lưu ý rằng để hỗ trợ việc sử dụng độ chính xác mở rộng này, một ngôn ngữ phải cung cấp cả chỉ báo về định dạng có sẵn rộng nhất, tốc độ hợp lý, để chương trình có thể chọn phương pháp sử dụng và các tham số môi trường cho biết độ chính xác và phạm vi của từng định dạng . g. , rằng nó có phạm vi rộng hơn gấp đôi)
  3. Sử dụng định dạng rộng hơn gấp đôi ngay cả khi nó phải được mô phỏng trong phần mềm. Đối với các chương trình phức tạp hơn ví dụ chuẩn Euclide, lập trình viên có thể chỉ muốn tránh viết hai phiên bản của chương trình và thay vào đó dựa vào độ chính xác mở rộng ngay cả khi nó chậm. Một lần nữa, ngôn ngữ phải cung cấp các tham số môi trường để chương trình có thể xác định phạm vi và độ chính xác của định dạng có sẵn rộng nhất
  4. Không sử dụng độ chính xác rộng hơn; . Đối với các chương trình được viết dễ dàng nhất phụ thuộc vào số học chính xác kép được làm tròn chính xác, bao gồm một số ví dụ được đề cập ở trên, ngôn ngữ phải cung cấp cách để lập trình viên chỉ ra rằng không được sử dụng độ chính xác mở rộng, mặc dù có thể tính toán kết quả trung gian . (Các kết quả trung gian được tính toán theo cách này vẫn có thể phải làm tròn hai lần nếu chúng bị tràn khi được lưu vào bộ nhớ. nếu kết quả của một phép toán số học trước tiên được làm tròn thành 53 bit có nghĩa, sau đó được làm tròn lại thành ít bit có nghĩa hơn khi nó phải được không chuẩn hóa, thì kết quả cuối cùng có thể khác với kết quả thu được bằng cách làm tròn chỉ một lần thành số không được chuẩn hóa. Tất nhiên, hình thức làm tròn kép này rất khó có thể ảnh hưởng xấu đến bất kỳ chương trình thực tế nào. )
  5. Làm tròn kết quả chính xác cho cả độ chính xác và phạm vi của định dạng kép. Việc thực thi nghiêm ngặt độ chính xác kép này sẽ hữu ích nhất cho các chương trình kiểm tra phần mềm số hoặc bản thân số học gần giới hạn của cả phạm vi và độ chính xác của định dạng kép. Các chương trình kiểm tra cẩn thận như vậy có xu hướng khó viết theo cách di động; . Do đó, một lập trình viên sử dụng hệ thống dựa trên mở rộng để phát triển phần mềm mạnh mẽ phải có thể di chuyển được cho tất cả các triển khai IEEE 754 sẽ nhanh chóng đánh giá cao khả năng mô phỏng số học của các hệ thống đơn/kép mà không cần nỗ lực phi thường

Không có ngôn ngữ hiện tại nào hỗ trợ tất cả năm tùy chọn này. Trên thực tế, một số ngôn ngữ đã cố gắng cung cấp cho lập trình viên khả năng kiểm soát việc sử dụng độ chính xác mở rộng. Một ngoại lệ đáng chú ý là ISO/IEC 9899. Ngôn ngữ lập trình 1999 - Tiêu chuẩn C, phiên bản mới nhất của ngôn ngữ C, hiện đang ở giai đoạn chuẩn hóa cuối cùng

Tiêu chuẩn C99 cho phép triển khai đánh giá các biểu thức ở định dạng rộng hơn định dạng thường được liên kết với loại của chúng, nhưng tiêu chuẩn C99 khuyến nghị sử dụng một trong ba phương pháp đánh giá biểu thức duy nhất. Ba phương pháp được đề xuất được đặc trưng bởi mức độ mà các biểu thức được "thăng cấp" thành các định dạng rộng hơn và việc triển khai được khuyến khích xác định phương pháp nào nó sử dụng bằng cách xác định macro tiền xử lý

     n = n/2
62. nếu
     n = n/2
62 là 0, mỗi biểu thức được đánh giá theo định dạng tương ứng với loại của nó; . (Việc triển khai được phép đặt
     n = n/2
62 thành -1 để chỉ ra rằng phương pháp đánh giá biểu thức là không thể xác định được. ) Tiêu chuẩn C99 cũng yêu cầu tệp tiêu đề
     n = n/2
72 xác định các loại
     n = n/2
73 và
     n = n/2
74, có độ rộng tối thiểu tương ứng là
     n = n/2
65 và
     n = n/2
176 và nhằm khớp với các loại được sử dụng để đánh giá các biểu thức
     n = n/2
65 và
     n = n/2
176. Ví dụ: nếu
     n = n/2
62 là 2, cả
     n = n/2
73 và
     n = n/2
74 đều là
     n = n/2
70. Cuối cùng, tiêu chuẩn C99 yêu cầu tệp tiêu đề
     n = n/2
72 xác định các macro tiền xử lý chỉ định phạm vi và độ chính xác của các định dạng tương ứng với từng loại dấu phẩy động

Sự kết hợp các tính năng được yêu cầu hoặc khuyến nghị bởi tiêu chuẩn C99 hỗ trợ một số trong năm tùy chọn được liệt kê ở trên nhưng không phải tất cả. Ví dụ: nếu một triển khai ánh xạ loại

     n = n/2
70 sang định dạng kép mở rộng và xác định
     n = n/2
62 là 2, thì lập trình viên có thể giả định một cách hợp lý rằng độ chính xác mở rộng tương đối nhanh, vì vậy các chương trình như ví dụ định mức Euclide có thể chỉ cần sử dụng các biến trung gian loại
     n = n/2
70 ( . Mặt khác, việc triển khai tương tự phải giữ các biểu thức ẩn danh ở độ chính xác mở rộng ngay cả khi chúng được lưu trữ trong bộ nhớ (e. g. , khi trình biên dịch phải tràn các thanh ghi dấu phẩy động) và nó phải lưu trữ kết quả của các biểu thức được gán cho các biến được khai báo
     n = n/2
176 để chuyển đổi chúng thành độ chính xác kép ngay cả khi chúng có thể được giữ trong các thanh ghi. Do đó, cả loại
     n = n/2
176 và loại
     n = n/2
74 đều không thể được biên dịch để tạo mã nhanh nhất trên phần cứng dựa trên mở rộng hiện tại

Tương tự như vậy, tiêu chuẩn C99 cung cấp các giải pháp cho một số vấn đề được minh họa bằng các ví dụ trong phần này nhưng không phải tất cả. Phiên bản tiêu chuẩn C99 của hàm

     n = n/2
91 được đảm bảo hoạt động chính xác nếu biểu thức
     n = n/2
190 
     n = n/2
044 
     n = n/2
006 được gán cho một biến (thuộc bất kỳ loại nào) và biến đó được sử dụng xuyên suốt. Tuy nhiên, một chương trình tiêu chuẩn C99 di động, hiệu quả để chia một số có độ chính xác kép thành các phần cao và thấp, tuy nhiên, khó khăn hơn. làm cách nào chúng tôi có thể phân tách ở đúng vị trí và tránh làm tròn hai lần nếu chúng tôi không thể đảm bảo rằng các biểu thức
     n = n/2
176 được làm tròn chính xác để đạt được độ chính xác gấp đôi? . Định lý 14 nói rằng chúng ta có thể tách ở bất kỳ vị trí bit nào miễn là chúng ta biết độ chính xác của phép tính cơ bản, và
     n = n/2
62 và macro tham số môi trường sẽ cung cấp cho chúng ta thông tin này

Đoạn sau đây cho thấy một triển khai có thể

     n = n/2
47
     n = n/2
47
     n = n/2
49
     n = n/2
50
     n = n/2
51
     n = n/2
52
     n = n/2
53
     n = n/2
54
     n = n/2
55
     n = n/2
56
     n = n/2
57
     n = n/2
58
     n = n/2
59
     n = n/2
0
     n = n/2
1

Tất nhiên, để tìm ra giải pháp này, lập trình viên phải biết rằng biểu thức

     n = n/2
176 có thể được đánh giá với độ chính xác mở rộng, rằng vấn đề làm tròn hai lần sau đó có thể khiến thuật toán gặp trục trặc và độ chính xác mở rộng đó có thể được sử dụng thay thế theo Định lý 14. Một giải pháp rõ ràng hơn chỉ đơn giản là xác định rằng mỗi biểu thức được làm tròn chính xác để tăng gấp đôi độ chính xác. Trên các hệ thống dựa trên mở rộng, điều này chỉ yêu cầu thay đổi chế độ chính xác làm tròn, nhưng thật không may, tiêu chuẩn C99 không cung cấp cách di động để thực hiện việc này. (Các bản nháp đầu tiên của Chỉnh sửa Dấu phẩy động C, tài liệu làm việc chỉ định các thay đổi được thực hiện đối với tiêu chuẩn C90 để hỗ trợ dấu phẩy động, khuyến nghị rằng việc triển khai trên các hệ thống có chế độ chính xác làm tròn cung cấp các hàm
     n = n/2
99 và
     n = n/2
300 để nhận và đặt . Đề xuất này đã bị xóa trước khi các thay đổi được thực hiện đối với tiêu chuẩn C99. )

Thật trùng hợp, cách tiếp cận của tiêu chuẩn C99 để hỗ trợ tính di động giữa các hệ thống có khả năng số học số nguyên khác nhau gợi ý một cách tốt hơn để hỗ trợ các kiến ​​trúc dấu phẩy động khác nhau. Mỗi triển khai tiêu chuẩn C99 cung cấp tệp tiêu đề

     n = n/2
72 xác định các loại số nguyên mà triển khai hỗ trợ, được đặt tên theo kích thước và hiệu quả của chúng. ví dụ:
     n = n/2
304 là loại số nguyên có độ rộng chính xác là 32 bit,
     n = n/2
305 là loại số nguyên nhanh nhất có độ rộng tối thiểu 16 bit của triển khai và
     n = n/2
306 là loại số nguyên rộng nhất được hỗ trợ. Người ta có thể tưởng tượng một sơ đồ tương tự cho các loại dấu phẩy động. ví dụ:
     n = n/2
307 có thể đặt tên cho loại dấu phẩy động với độ chính xác chính xác là 53 bit nhưng có thể có phạm vi rộng hơn,
     n = n/2
308 có thể đặt tên cho loại triển khai nhanh nhất với độ chính xác ít nhất là 24 bit và
     n = n/2
309 có thể đặt tên cho loại nhanh hợp lý rộng nhất được hỗ trợ. Các loại nhanh có thể cho phép trình biên dịch trên các hệ thống dựa trên mở rộng tạo mã nhanh nhất có thể chỉ tuân theo ràng buộc rằng giá trị của các biến được đặt tên không được thay đổi do tràn thanh ghi. Các loại chiều rộng chính xác sẽ khiến trình biên dịch trên các hệ thống dựa trên mở rộng đặt chế độ làm tròn độ chính xác thành làm tròn theo độ chính xác đã chỉ định, cho phép phạm vi rộng hơn chịu cùng một ràng buộc. Cuối cùng,
     n = n/2
74 có thể đặt tên cho một loại có cả độ chính xác và phạm vi của định dạng kép IEEE 754, cung cấp đánh giá kép nghiêm ngặt. Cùng với các macro tham số môi trường được đặt tên phù hợp, sơ đồ như vậy sẽ sẵn sàng hỗ trợ tất cả năm tùy chọn được mô tả ở trên và cho phép người lập trình chỉ ra ngữ nghĩa dấu phẩy động mà chương trình của họ yêu cầu một cách dễ dàng và rõ ràng.

Hỗ trợ ngôn ngữ cho độ chính xác mở rộng có phức tạp như vậy không? . Tuy nhiên, các hệ thống dựa trên mở rộng đặt ra những lựa chọn khó khăn. chúng không hỗ trợ tính toán chính xác kép thuần túy cũng như tính toán chính xác mở rộng thuần túy hiệu quả như hỗn hợp của cả hai và các chương trình khác nhau yêu cầu các hỗn hợp khác nhau. Hơn nữa, không nên để việc lựa chọn thời điểm sử dụng độ chính xác mở rộng cho những người viết trình biên dịch, những người thường bị cám dỗ bởi các điểm chuẩn (và đôi khi được các nhà phân tích số nói thẳng ra) coi số học dấu phẩy động là "không chính xác vốn có" và do đó không xứng đáng cũng như không có khả năng. . Thay vào đó, sự lựa chọn phải được trình bày cho các lập trình viên và họ sẽ yêu cầu các ngôn ngữ có khả năng thể hiện sự lựa chọn của họ.

Phần kết luận

Các nhận xét ở trên không nhằm chê bai các hệ thống dựa trên mở rộng mà để phơi bày một số sai lầm, điều đầu tiên là tất cả các hệ thống IEEE 754 phải cung cấp các kết quả giống hệt nhau cho cùng một chương trình. Chúng tôi đã tập trung vào sự khác biệt giữa hệ thống dựa trên mở rộng và hệ thống đơn/kép, nhưng còn có sự khác biệt nữa giữa các hệ thống trong mỗi họ này. Ví dụ: một số hệ thống đơn/đôi cung cấp một lệnh duy nhất để nhân hai số và cộng số thứ ba chỉ với một lần làm tròn cuối cùng. Thao tác này, được gọi là phép cộng-thêm kết hợp, có thể khiến cùng một chương trình tạo ra các kết quả khác nhau trên các hệ thống đơn/kép khác nhau và giống như độ chính xác mở rộng, nó thậm chí có thể khiến cùng một chương trình tạo ra các kết quả khác nhau trên cùng một hệ thống tùy thuộc vào việc . (Một phép cộng nhân hợp nhất cũng có thể làm hỏng quá trình tách của Định lý 6, mặc dù nó có thể được sử dụng theo cách không di động để thực hiện nhiều phép nhân chính xác mà không cần tách. ) Mặc dù tiêu chuẩn IEEE không lường trước được hoạt động như vậy, nhưng nó vẫn tuân thủ. sản phẩm trung gian được chuyển đến một "điểm đến" ngoài tầm kiểm soát của người dùng đủ rộng để giữ chính xác sản phẩm đó và tổng cuối cùng được làm tròn chính xác để khớp với điểm đến chính xác đơn hoặc kép

Ý tưởng rằng IEEE 754 quy định chính xác kết quả mà một chương trình nhất định phải cung cấp dù sao cũng rất hấp dẫn. Nhiều lập trình viên muốn tin rằng họ có thể hiểu hành vi của một chương trình và chứng minh rằng nó sẽ hoạt động bình thường mà không cần tham khảo trình biên dịch biên dịch nó hoặc máy tính chạy nó. Theo nhiều cách, hỗ trợ niềm tin này là một mục tiêu đáng giá cho các nhà thiết kế hệ thống máy tính và ngôn ngữ lập trình. Thật không may, khi nói đến số học dấu phẩy động, mục tiêu hầu như không thể đạt được. Các tác giả của các tiêu chuẩn IEEE biết điều đó và họ đã không cố gắng đạt được nó. Kết quả là, mặc dù gần như tuân thủ (hầu hết) tiêu chuẩn IEEE 754 trong toàn ngành công nghiệp máy tính, các lập trình viên của phần mềm di động vẫn phải tiếp tục đối phó với số học dấu chấm động không thể đoán trước.

Nếu các lập trình viên khai thác các tính năng của IEEE 754, họ sẽ cần các ngôn ngữ lập trình giúp dự đoán số học dấu phẩy động. Tiêu chuẩn C99 cải thiện khả năng dự đoán ở một mức độ nào đó với chi phí yêu cầu các lập trình viên viết nhiều phiên bản chương trình của họ, mỗi phiên bản cho một

     n = n/2
62. Liệu các ngôn ngữ trong tương lai có chọn thay vào đó để cho phép các lập trình viên viết một chương trình duy nhất với cú pháp thể hiện rõ ràng mức độ phụ thuộc vào ngữ nghĩa của IEEE 754 hay không vẫn còn phải xem. Các hệ thống dựa trên mở rộng hiện tại đe dọa triển vọng đó bằng cách cám dỗ chúng ta giả định rằng trình biên dịch và phần cứng có thể biết rõ hơn lập trình viên về cách thực hiện tính toán trên một hệ thống nhất định. Giả định đó là sai lầm thứ hai. độ chính xác được yêu cầu trong kết quả tính toán không phụ thuộc vào máy tạo ra nó mà chỉ phụ thuộc vào kết luận rút ra từ nó, và của người lập trình, trình biên dịch và phần cứng, tốt nhất chỉ có người lập trình mới có thể biết những kết luận đó có thể là gì