Python phao điện

Bài trước đã giới thiệu về các phép tính tiểu học (cộng trừ nhân chia), bài này sẽ giới thiệu các phép tính phổ biến thông tin có vẻ cao cấp hơn như phép thừa, chia lấy dư, tính toán số phức

Disposables

Khả năng tính toán thừa, hay phụ, tiếng Anh gọi là "power". 2 mũ 3 trong tiếng Anh là "2 mũ 3". Subsignal trong Python sử dụng ký hiệu n = n/2 15, KHÔNG ký hiệu n = n/2 16

>>> 2 ** 7 128 >>> 2 ** 8 256 >>> 10 ** 7 10000000 >>> 2 ** 1000 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376

With Python, large size of number is tùy ý, không có giới hạn. Nếu bạn còn chưa quên toán thì sẽ biết rằng phép căn bậc 2 (căn bậc hai) thực chất là mũ 1/2, căn bậc 3 (căn bậc ba) là mũ 1/3,

>>> 4 ** (1/2) 2.0 >>> 27 ** (1/3) 3.0 >>> 2 ** (1/2) 1.4142135623730951 >>> 4 ** 2.0 16.0

Chú ý, giống các phép toán khác, phép phụ khi thực hiện với float ở phần số hoặc phần phụ đều trả về kết quả kiểu float

Phép chia lấy dư

Phép chia lấy dư (modulo hay viết tắt là mod) trong Python được biểu diễn bằng dấu n = n/2 17

Sử dụng phép chia lấy dư, ta có thể kiểm tra một số có là bội của các số khác nếu nó chia lấy dư được kết quả là 0

Ứng dụng phổ biến biến thường gặp là kiểm tra một số là ngày (chẵn) hay lẻ (lẻ)

Do dư luôn nhỏ hơn số chia (7 là số bị chia, 2 là số bị chia, 1 là số dư), cho phép chia dư được sử dụng khi làm bài toán có quy luật tuần hoàn. Cho đề bài

Biết giữa nhà Mít sang nhà Chuối có 9 bậc (đánh số từ 1-9), coi nhà Mít là bậc 0 và nhà Chuối là bậc 10. Biết từng 1 bước đi 1 bậc, hỏi nếu Mít bắt đầu từ nhà mình, đi theo đường ấy, đến nhà Chuối thì quay về, thì sau 1235789 bước, Mít đang ở bậc mấy?

[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11]

Ta có thể giải bài toán này bằng cách đếm số lần, cho đến khi hết 1235789 bước thì mới biết Mít đang ở phương trời nào, hoặc chỉ cần dùng phép chia dư để loại bỏ những phần tuần hoàn của bài toán. Do Mít đi 20 bước (10 bước đi, 10 bước về) sẽ trở về bậc số 0. Vì vậy cứ chia lấy dư cho 20, ta thu được số bước lẻ còn lại

Sau 21 bước, Mít đang ở bậc số 21 % 20 == 1. Sau 31 bước, Mít đang ở bậc số 31 % 20 == 11, tức thì sau 1 bước trên đường về hay ở bậc số 9. Sau 1235789, Mít ở bậc

Hay một bài toán đơn giản khác cũng sử dụng phép n = n/2 17 để giải. if hôm nay là thứ 6 thì 96 ngày nữa là thứ mấy?

Phép modulo còn ứng dụng quan trọng trong ngành mật mã học (cryptography), hệ thống bảo mật RSA có sử dụng phép modulo, xem thêm tại

Redivisable integer

Phép chia chỉ lấy phần nguyên (phân chia tầng) sử dụng ký hiệu n = n/2 19

Number of

Số phức (được học trong chương trình toán cấp 3) cũng được hỗ trợ bởi Python, phần ảo được ký hiệu bằng chữ n = n/2 20

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 chấm độ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 mức 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ở

(luôn được coi là chẵn) và độ chính xác p. Nếu
= 10 và p = 3 thì số 0. 1 được biểu diễn là 1. 00×10-1. Nếu
= 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 ×

e, trong đó d. đ. d được gọi là số có nghĩa và có p chữ số. Chính xác hơn ± d0. d1 d2. dp-1 ×
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ó

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

= 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
×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

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
 = 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


HÌNH D-1 Số chuẩn hóa khi
= 2, p = 3, emin = -1, emax = 2

Lỗi tương đối và Ulps 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 

 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

= 10, p = 3 và emin = -98. Các số x = 6. 87 × 10-97 và y = 6. 81 × 10-97 dường như là các số dấu phẩy động hoàn toàn bình thường, lớn hơn 10 lần 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
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
x - y = 0 ?

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

  n = n/2 309  n = n/2 310  n = n/2 311  n = n/2 304  n = n/2 313, 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 
 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

= 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

= 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ề

= 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.


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ố

. 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
. 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 được hoạt động tốt trong phạm vi này khi sử dụng tràn dầ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

· tôi

gặp phải vấn đề là nếu một trong hai thành phần của mẫu số c + id lớn hơn, 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)

Á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à

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±
hoặc ±xmaxround(x2-
)underflow0,hoặc
)divide by zero±
operandsinvalidNaNoperandsinexactround(x)round(x)

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

= 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

= 10, p = 3, 3. 5
4. 2 = 14. 7 là chính xác, nhưng 3. 5 
 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 314  n = n/2 315  n = n/2 316  n = n/2 303 n = n/2 318 n = n/2 319. Vì so sánh NaN với một số có , hoặc = (chứ không phải ) luôn trả về false .

, >,
, or = (but not
) always returns false, this code will go into an infinite loop if n = n/2 306 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

, 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
. Số mũ
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 +

và làm tròn về phía -
. Khi được sử dụng với thao tác chuyển đổi thành số nguyên, làm tròn về phía -
làm cho phép chuyển đổi trở thành hàm sàn, trong khi làm tròn về phía +
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ề -
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 +
. Similarly, overflows of negative magnitude will produce the largest negative number when round toward +
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

y được làm tròn về phía -
, và x
y rounded toward +
. 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 -
, và với chế độ làm tròn được đặt thành làm tròn về phía +
.

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 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 ngày càng 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 +

, làm tròn về 0 và làm tròn về -
. 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 2______33[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 4 n = n/2 n = n/2 6 n = n/2 7 n = n/2 8 n = n/2 >>> 4 ** (1/2) 2.0 >>> 27 ** (1/3) 3.0 >>> 2 ** (1/2) 1.4142135623730951 >>> 4 ** 2.0 16.0 1[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 4 n = n/2 30 n = n/2 6

Nếu n < 0, thì cách chính xác hơn để tính xn không phải là gọi n = n/2 321 n = n/2 322 mà là gọi n = n/2 323 n = n/2 322, 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 325 n = n/2 322 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 323 n = n/2 322. 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 ________ 3321 ________ 3322, đ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
.

Nếu arctan(

) ước tính thành
/2, thì arccos(-1) sẽ ước tính chính xác thành 2·arctan(
) =
, 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 32 n = n/2 33 n = n/2 34

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 331 n = n/2 332. 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.

|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

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
· · ·then r = 1, and 1 is subtracted from [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 451 because of the borrow, so the result is
kx. Cuối cùng hãy xem xét trường hợp [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 449 =. Nếu r = 0 thì [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 451 là chẵn, [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 462 là lẻ và chênh lệch được làm tròn lên, cho kết quả
kx. Tương tự khi r = 1, [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 451 là số lẻ, [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 462 là số chẵn, chênh lệch được làm tròn xuống, do đó, một lần nữa chênh lệch là
kx. Tóm lại(34) (m
x)x =
kx
Kết hợp các phương trình và cho (m
x) - (m
xx) = x - x mod(
k) +
·
k. The result of performing this computation is[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 465 bb...bb
______2466Quy tắc tính r, phương trình (33), cũng giống như quy tắc làm tròn [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 467 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 468 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 +
kx không thực hiện. Khi x +
kx thực hiện, thì mx =
kx + x trông như thế này. [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 444aa. aabb. bb
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 470Như vậy, m
x = mx - x mod(
k) + w
k, where w = -Z if Z <
/2, but the exact value of w is unimportant. Next, m
x - x =
kx - x mod(
k) + w
k. In a picture[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 471 w
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 472Làm tròn cho (m
x)x =
kx + w
k - r
k, where r = 1 if [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 449 > or if [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 449 =and b0 = 1. Finally,(m
x) - (m
xx) = mx - x mod(
k) + w
k - (
kx + w
k - r
k)
= x - x mod(
k) + r
k.
Và một lần nữa, r = 1 chính xác khi làm tròn [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 475 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 n = n/2 2 n = n/2 38 n = n/2 4 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 =
xj (1 +
j) + O(N
2)
|xj|, where |
j.
2
.

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
xi đã diễn ra như thế nào. Giới thiệu s1 = x1, si = (1 +
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
j's. Hệ số chính xác của x1 là (1 +
2)(1 +
3). (1 +
n), do đó, bằng cách đánh số lại, hệ số của x2 phải là (1 + 
3)(1 +
4) .. (1 +
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 +
k)sk = sk - 1
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 toán hệ số của x1 trong sk lại dễ dàng hơn - ck và ck. Khi k = 1,c1 = (s1(1 +
k)ck = (sksk - 1)yk= [(sk - sk - 1) (1 +
k) - yk] (1 +
k)where all the Greek letters are bounded by
. 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 +
1) - y1) (1 + d1)= y1((1 + s1) (1 +
1) - 1) (1 + d1)= x1(s1 +
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
+ O(
2)
S1 = +
1 -
1 + 4
2 + O(
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 +
k)= [sk - 1 + (xk - ck - 1) (1 +
k)](1 +
k)= [(sk - 1 - ck - 1) -
kck - 1](1+
k)ck = [{sk - sk - 1}(1 +
k) - yk](1 +
k)= [{((sk - 1 - ck - 1) -
kck - 1)(1 +
k) - sk - 1}(1 +
k) + ck - 1(1 +
k)](1 + 
k)= [{(sk - 1 - ck - 1)
k -
kck-1(1 +
k) - ck - 1}(1 +
k) + ck - 1(1 +
k)](1 +
k)= [(sk - 1 - ck - 1)
k(1 +
k) - ck - 1(
k +
k(
k +
k +
k
k))](1 +
k),sk - ck = ((sk - 1 - ck - 1) -
kck - 1) (1 +
k)- [(sk - 1 - ck - 1)
k(1 +
k) - ck - 1(
k +
k(
k +
k +
k
k)](1 +
k)= (sk- 1 - ck - 1)((1 +
k) -
k(1 +
k)(1 +
k))+ ck - 1(-
k(1 +
k) + (
k +
k(
k +
k +
k
k)) (1 +
k))= (s- 1 - ck - 1) (1 -
k(
k +
k +
k
k))+ ck - 1 - [
k +
k +
k(
k +
k
k) + (
k +
k(
k +
k +
k
k))
k]Since Sk and Ck are only being computed up to order
2, these formulas can be simplified toCk= (
k + O(
2))Sk - 1 + (-
k + O(
2))Ck - 1Sk= ((1 + 2
2 + O(
3))Sk - 1 + (2
+
(
2))Ck - 1Using these formulas givesC2 =
2 + O(
2)
S2 = 1 +
1 -
1 + 10
2 + O(
3)
and in general it is easy to check by induction thatCk =
k + O(
2)
Sk = 1 +
1 -
1 + (4k+2)
2 + O(
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 +

1 -
1 + (4n + 2)
2 = (1 + 2
+
(n
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 quan niệm sai lầm có thể có 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. Thứ nhất, 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 60 n = n/2 61____562 n = n/2 63 n = n/2 64 n = n/2 65 n = n/2 9

Ở đây các 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476. 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 353 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 353, 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476, 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 353, thì nó có thể được lưu trữ trong bộ nhớ và vì n = n/2 353 được khai báo là [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476, 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 353, 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 353 hoặc nó có thể giữ n = n/2 353 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 335 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á 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 335 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 335 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 335 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?

n = n/2 67 n = n/2 68 n = n/2 69 n = n/2 70 n = n/2 71 n = n/2 72____573 n = n/2 74

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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 ở dòng thứ ba với độ chính xác mở rộng và so sánh kết quả với [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490. 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 306 không nhỏ đến mức [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 làm tròn thành [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490 với độ chính xác mở rộng nhưng đủ nhỏ để [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 làm tròn thành [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490 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 306 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306, được đánh giá theo độ chính xác mở rộng. Trong trường hợp đó, nếu n = n/2 306 nhỏ nhưng không đủ nhỏ để [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 làm tròn thành [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490 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ị đúng gần bằng n = n/2 306 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 306 là 2-24 + 2-47, vì vậy, n = n/2 306 là số chính xác đơn lẻ nhỏ nhất sao cho [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 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 344  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 306, 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 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 chính xác [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 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. )

. Similarly, suppose the rest of the expression in the sixth line, including the reoccurrence of the subexpression [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306, is evaluated in extended precision. In that case, if n = n/2 306 is small but not quite small enough that [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 rounds to [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490 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 306, and again the relative error can approach one. For a concrete example, take n = n/2 306 to be 2-24 + 2-47, so n = n/2 306 is the smallest single precision number such that [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 rounds up to the next larger number, 1 + 2-23. Then n = n/2 20  n = n/2 344  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 306, 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 đượ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 điều 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 theo độ 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 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à làm tròn kép. Ở 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. )

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 342 là các biến dấu phẩy động với. n = n/2 50.

. n = n/2 342. và chúng tôi tính toán.

________ 575 ________ 576

sau đó trong hầu hết các phép tính, n = n/2 54 khôi phục chính xác lỗi làm tròn xảy ra trong máy tính n = n/2 55. 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 342 = 1/2 - 2-54, thì n = n/2 50  n = n/2 344  n = n/2 342 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)
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

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 có nghĩa nhỏ 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

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.
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.
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.
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.
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

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.
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.
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

d, điều này ngụ ý. ne.
1/4. Nếu k > d, ta có. ne.
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

d, điều này ngụ ý. ne.
1/2. Nếu k > d, ta có. ne.
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ù kết quả trung gian có thể được tính toán . (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, thì 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à [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476, đồng thời 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à [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476. 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à định nghĩa 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ụ về định mức Euclide có thể chỉ cần sử dụng các biến trung gian của 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476 để 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 490  n = n/2 344  n = n/2 306 đượ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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476 đượ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 toán cơ bản và các macro tham số môi trường và n = n/2 62 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 77 n = n/2 77_______579 n = n/2 80 n = n/2 81 n = n/2 82 n = n/2 83 n = n/2 84 n = n/2 85 n = n/2 86 n = n/2 87 n = n/2 88 n = n/2 89 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 [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] [ 9] [10] [19] [18] [17] [16] [15] [14] [13] [12] [11] 476 có thể được đánh giá ở độ 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 600 để lấy 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 604 là loại số nguyên có độ rộng chính xác là 32 bit, n = n/2 605 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 606 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 607 có thể đặt tên cho loại dấu phẩy động với độ chính xác chính xác 53 bit nhưng có thể có phạm vi rộng hơn, n = n/2 608 có thể đặt tên cho loại triển khai nhanh nhất với độ chính xác ít nhất 24 bit và n = n/2 609 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

Những nhận xét ở trên không nhằm mục đích chê bai các hệ thống dựa trên mở rộng mà để vạch trần 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 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. Hoạt động 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 chính xác 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ừ đó 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ì

Chủ đề