Floating Point Number Là Gì ? Một Số Dấu Phẩy Động Bất Thường Là Gì

Xác định xem đối số dấu phẩy động đã cho có bình thường hay không, nghĩa là không phải 0, thấp hơn, vô hạn hay nan.

Bạn đang xem: Số dấu phẩy động là gì

Một số bằng 0, vô hạn hoặc nan là hiển nhiên. Nhưng nó cũng nói lên điều bí ẩn. Không biết là khi nào?

Trong tiêu chuẩn IEEE754, các số dấu phẩy động được biểu diễn dưới dạng ký hiệu khoa học nhị phân x = m × 2 e. Ở đây m là phần định trị và e là số mũ. Nói một cách toán học, bạn luôn có thể chọn số mũ sao cho 1 ≤ m e min. Những con số này là bất thường hoặc không bình thường.

Trên thực tế, phần định trị được lưu trữ mà không có số 1 đứng đầu, bởi vì ngoại trừ các siêu đại lượng (và số không), luôn có các số 1 đứng đầu. Vì vậy, lời giải thích là có một ẩn số đứng đầu 1 nếu số mũ không phải là nhỏ nhất và không có nếu số mũ là nhỏ nhất và số là một hàm

*) Nói chung hơn, 1 phút

Kiến thức cơ bản về ieee 754

Trước tiên, chúng ta hãy xem xét khái niệm cơ bản về số có tổ chức IEEE 754.

Chúng tôi sẽ tập trung vào độ chính xác duy nhất (32-bit), nhưng mọi thứ có thể được khái quát hóa cho các phân đoạn khác ngay lập tức.

Định dạng là:

Hoặc nếu bạn thích hình ảnh này:

Nguồn.

Ký hiệu rất đơn giản: 0 là tích cực, 1 là tiêu cực và câu chuyện đã kết thúc.

Số mũ dài 8 bit, vì vậy nó nằm trong khoảng từ 0 đến 255.

Số mũ được coi là sai lệch vì phần bù của nó là -127, ví dụ:

0 == trường hợp đặc biệt: số không hoặc hàm dưới, được hiểu như sau 1 == 2 ^ -126 … 125 == 2 ^ -2126 == 2 ^ -1127 == 2 ^ 0128 == 2 ^ 1129 = = 2 ^ 2 … 254 == 2 ^ 127255 == trường hợp đặc biệt: infinity và nan quy ước bit trên cùng

Trong khi thiết kế IEEE 754, các kỹ sư nhận thấy rằng tất cả các số ngoại trừ số 0,0 đều có số nhị phân 1 là chữ số đầu tiên. Ví dụ:

25.0 == (nhị phân) 11001 == 1.1001 * 2 ^ 4 0.625 == (nhị phân) 0.101 == 1.01 * 2 ^ -1 tất cả đều bắt đầu bằng 1. Đó là một phần khó chịu.

Vì vậy, sẽ thật lãng phí nếu con số đó chiếm gần như mọi chữ số chính xác.

Vì lý do này, họ đã tạo ra “Quy ước Bit cao nhất”:

Luôn giả sử các số bắt đầu bằng dấu

Vậy phải làm gì với 0.0? Chà, họ quyết định tạo một ngoại lệ:

Vì vậy, các byte 00 00 00 00 cũng đại diện cho 0,0, có vẻ tốt.

Nếu chúng ta chỉ xem xét các quy tắc này, số khác 0 nhỏ nhất có thể được biểu diễn sẽ là:

Do quy ước bit đứng đầu, nó trông giống như thế này dưới dạng số thập phân thập lục phân:

1,000002 * 2 ^ (-127) trong đó .000002 là 22 số 0 và 1 ở cuối.

Chúng ta không thể lấy fraction = 0, nếu không số sẽ là 0.0.

Tuy nhiên, các kỹ sư có óc thẩm mỹ sắc sảo không kém đã nghĩ: Điều đó không tệ phải không? Chúng ta có đang nhảy trực tiếp từ 0,0 sang một thứ gì đó thậm chí không phải là lũy thừa thích hợp của 2 không? Bằng cách nào đó, chúng ta không thể biểu diễn các số nhỏ hơn?

Các con số bất thường

Các kỹ sư vò đầu bứt tai và trở lại như thường lệ với một ý tưởng hay khác. Điều gì sẽ xảy ra nếu chúng tôi tạo một quy tắc mới:

Nếu số mũ bằng 0, thì:

Những con số như vậy được gọi là siêu bền (hoặc đồng nghĩa với nonconstant).

Quy tắc này ngay lập tức ngụ ý những con số như sau:

vẫn là 0,0, thanh lịch hơn một chút vì nó có nghĩa là ít quy tắc phải tuân theo hơn.

Vì vậy, 0,0 thực sự là siêu hằng số mà chúng tôi đã xác định!

Sau đó, với quy tắc mới này, số không chuẩn nhỏ nhất là:

Trình bày:

1.0 * 2 ^ (-126) Khi đó, hình lập phương lớn nhất là:

Bằng:

0.fffffe * 2 ^ (-126) trong đó .fffffe lại là 23 bit ở bên phải dấu chấm.

Con số này rất gần với số lượng ngoại lệ tối thiểu và nghe có vẻ bình thường.

Số con nhỏ nhất khác 0 là:

Bằng:

0,000002 * 2 ^ (-126) cũng có vẻ rất gần với 0,0!

Không thể tìm thấy bất kỳ cách hợp lý nào để biểu diễn những con số nhỏ hơn thế, các kỹ sư đã rất vui mừng và quay lại tìm kiếm những bức ảnh về mèo trên mạng hoặc bất cứ điều gì họ đã làm trong 70 năm.

Như bạn có thể thấy, siêu số thường có sự cân bằng giữa độ chính xác và độ dài biểu diễn.

Là ví dụ tiêu biểu nhất, hàm con nhỏ nhất là hàm khác 0:

0,000002 * 2 ^ (-126) về cơ bản có một bit chính xác thay vì 32. Ví dụ: nếu chúng ta chia nó cho hai:

0,000002 * 2 ^ (-126) / 2 Chúng tôi thực sự gần bằng 0,0!

Hãy tưởng tượng

Luôn luôn là một ý tưởng hay khi có trực giác hình học về những gì chúng ta đang học, vì vậy hãy tiếp tục.

Nếu chúng ta vẽ biểu đồ 754 float trên mỗi dòng cho mỗi số mũ đã cho, nó sẽ giống như sau:

+ – + – + – + – + index | 126 | 127 | 128 | 129 | + – + – + – + – + | | | | | v v v v v v -floats ***** * * * * * * * * * * * * – ^ ^ ^ ^ | | | | | 0.5 1.0 2.0 4.0 8.0 Từ đó ta thấy:

Bây giờ, hãy hạ nó xuống chỉ mục 0.

Nếu không có bất thường, giả sử nó trông giống như sau:

+ – + – + – + – + – + số mũ |? | 0 | 1 | 2 | 3 | + – + – + – + – + – + | | | | | | v v v v v v v v v-v-v-v-v-v-v-v-v-v v * * * * * * * * * * * * * – ^ ^ ^ ^ ^ ^ | | | | | 0 | 2 ^ -126 2 ^ -125 2 ^ -124 2 ^ -123 | 2 ^ -127 Bất thường , nó trông như thế này:

+ – + – + – + – + chỉ số | 0 | 1 | 2 | 3 | + – + – + – + – + | | | | | v v v v v v -floats * * * * * * * * * * * * * * * * * – ^ ^ ^ ^ ^ ^ | | | | | | 0 | 2 ^ -126 2 ^ -125 2 ^ -124 2 ^ -123 | 2 ^ -127 Bằng cách so sánh hai biểu đồ, chúng ta thấy rằng:

Số bổ sung tăng gấp đôi độ dài của phạm vi số mũ 0, từ & lt; 2 ^ -127, 2 ^ -126) thành & lt; 0, 2 ^ -126)

Khoảng trắng giữa các float trong miền công cộng thường giống như & lt; 0, 2 ^ -126).

Xem thêm: Tiếng Anh là gì? Dùng cũng được, quá thích hợp

dải ô & lt; 2 ^ -127, 2 ^ -126) có một nửa số điểm mà không có số phụ.

Một nửa trong số chúng sẽ lấp đầy nửa còn lại của phạm vi.

dải ô <0, 2 ^ -127) Một số điểm là không bình thường, nhưng không phải.

Điểm thiếu sót của

này <0, 2 ^ -127) không được thanh lịch cho lắm và là lý do chính khiến công cụ phụ tồn tại!

Vì các điểm cách đều nhau:

Khi chúng tôi nói siêu chuẩn là sự cân bằng giữa kích thước và độ chính xác, đó là ý của chúng tôi.

Ví dụ c có thể chạy được

Bây giờ, hãy kiểm tra lý thuyết của chúng tôi với một số mã thực tế.

Trên hầu hết các máy tính để bàn và máy tính hiện tại, c float đại diện cho một số dấu phẩy động IEEE 754 chính xác một lần.

Điều này dành riêng cho máy tính xách tay lenovo p51 chạy ubuntu 18.04 amd64 của tôi.

Với giả định này, tất cả các xác nhận được chuyển đến chương trình sau:

subnormal.c

#if __stdc_version__ #error c11 started # endif # ifndef __stdc_iec_559 __ # error ieee 754 chưa được triển khai # endif # include #include / * flt_has_subnorm * / # include #include / * isnormal * / # include #include #if flt_has_subnorm! 1 # lỗi float không có số bất thường #endiftypedef struct {uint32_t sign, exponent, fraction;} float32; float32 float32_from_float (float f) {uint32_t byte; float32float32; byte = * (uint32_t *) & amp; f; float32 .fraction = byte & amp ; 0x007fffff; byte & gt; & gt; = 23; float32.exponent = byte & amp; 0x000000ff; byte & gt; & gt; = 8; float32.sign = byte & amp; 0x000000001; byte & gt;> = 1; trả về float32;} dấu float số mũ, phân số uint32_t) {uint32_t byte; byte = 0; byte | = dấu hiệu; byte 8; byte | = exponent; byte 23; byte | = fraction; return * (float *) & amp; byte;} int float32_equal (float f , dấu uint32_t, uint32_t mũ, uint32_t phân số) {float32 float32; float32 = float32_from_float (f); return (float32.sign == sign) & amp; & amp; (float32.exponent == exponent) & amp; & amp; (float32.fraction == fraction);} void float32_print (float f) {float32 float32 = float32_fr om_float (f); printf (“%” priu32 “%” priu32 “%” priu32 ” n”, float32.sign, float32.exponent, float32.fraction);} int main (void) {/ * Ví dụ cơ bản. * / khẳng định (float32_equal (0.5f, 0, 126, 0); khẳng định (float32_equal (1.0f, 0, 127, 0); khẳng định (float32_equal (2.0f, 0, 128, 0); khẳng định (bình thường (0.5f) ))); khẳng định (bình thường (1.0f)); khẳng định (bình thường (2.0f)); / * Xem xét nhanh các ký tự dấu phẩy động c hex. * / khẳng định (0.5f == 0x1.0p-1f); khẳng định ( 1.0f == 0x1.0p0f); khẳng định (2.0f == 0x1.0p1f); / * bit dấu. * / Khẳng định (float32_equal (-0.5f, 1, 126, 0)); khẳng định (float32_equal (-1.0) f, 1, 127, 0); khẳng định (float32_equal (-2.0f, 1, 128, 0); khẳng định (bình thường (-0.5f)); khẳng định (bình thường (-1.0f)); khẳng định (bình thường (-2.0 ) f)); / * trường hợp đặc biệt của 0,0 và -0,0. * / khẳng định (float32_equal (0.0f, 0, 0, 0); khẳng định (float32_equal (-0.0f, 1, 0, 0); khẳng định (! isnormal (0.0f)); khẳng định (! Isnormal (-0.0f)); khẳng định (0.0f == -0.0f); / * acular định nghĩa flt_min là số bất thường nhỏ nhất. * / Khẳng định (flt_min == 0x1 .0p- 126f); khẳng định (float32_equal (flt_min, 0, 1, 0); khẳng định (bình thường (flt_min)); / * số siêu thường tối đa. * / Float Maximum_subnormal = float_from_bytes (0, 0, 0x7fffff); khẳng định (con số bất thường lớn nhất == 0x0. exit_success;} github ngược dòng.

Biên dịch và chạy:

gcc -ggdb3 -o0 -std = c11 -wall -wextra -wpedantic -werror -o subnormal.out subnormal.c./subnormal.out<…c++

Ngoài việc hiển thị tất cả c api, c ++ cũng hiển thị một số hàm con liên quan đến các hàm con không có sẵn trong c, ví dụ:

Trong C ++, toàn bộ api được tạo mẫu cho từng loại dấu phẩy động và tốt hơn.

Triển khai

x86_64 và armv8 tích hợp ieee 754 trực tiếp trên phần cứng, mã c được dịch sang.

Subnormals dường như chậm hơn bình thường trong một số triển khai: tại sao việc thay đổi 0,1f thành 0 lại làm chậm hiệu suất theo hệ số 10? Điều này được đề cập trong hướng dẫn sử dụng tay, hãy xem phần “chi tiết armv8” của câu trả lời này.

Thông tin chi tiết về armv8

Cẩm nang Tham khảo Kiến trúc armv8 ddi 0487c.a a1.5.4 Sách hướng dẫn “flush-to-zero” mô tả chế độ có thể định cấu hình trong đó các khẩu súng phụ được làm tròn thành 0 để cải thiện hiệu suất:

Hiệu suất xử lý dấu phẩy động có thể giảm khi thực hiện các phép tính liên quan đến các số không chuẩn hóa và các ngoại lệ có lưu lượng truy cập thấp. Trong nhiều thuật toán, hiệu suất này có thể được khôi phục bằng cách thay thế các toán hạng không chuẩn hóa và kết quả trung gian bằng các số không mà không ảnh hưởng đáng kể đến độ chính xác của kết quả cuối cùng. Để đạt được sự tối ưu hóa này, nhánh triển khai dấu phẩy động cho phép sử dụng chế độ rõ ràng cho các định dạng dấu phẩy động khác nhau, như sau:

Đối với aarch64:

Nếu fpcr.fz == 1, chế độ xóa được sử dụng cho tất cả các đầu vào và đầu ra chính xác đơn và kép cho tất cả các lệnh.

Nếu fpcr.fz16 == 1, hãy sử dụng chế độ xóa cho tất cả các đầu vào và đầu ra nửa chính xác của lệnh dấu phẩy động, ngoại trừ: – Chuyển đổi giữa nửa chính xác và đơn chính xác. – Chuyển đổi giữa số chính xác nửa và số chính xác kép.

a1.5.2 “Tiêu chuẩn và thuật ngữ dấu chấm động” Bảng a1-3 “Thuật ngữ dấu chấm động” xác nhận rằng các ký hiệu con và ký hiệu là từ đồng nghĩa:

Sổ tay hướng dẫn này ieee 754-2008- – & lt; … & gt; unsrmal, hoặc subnormalized subnormalc5.2.7 “fpcr, thanh ghi điều khiển dấu chấm động” mô tả cách armv8 xử lý các ngoại lệ dấu chấm động trên đầu vào:

fpcr.ide, bit & lt; 15 & gt; input kích hoạt bẫy ngoại lệ dấu chấm động ngoại lệ. Các giá trị có thể có là:

0b0 Xử lý ngoại lệ mở rộng được chọn. Nếu một ngoại lệ dấu chấm động xảy ra, bit fpsr.idc được đặt thành 1.

0b1 Xử lý ngoại lệ bắt được chọn. Nếu một ngoại lệ dấu chấm động xảy ra, pe không cập nhật các bit fpsr.idc. Phần mềm xử lý bẫy có thể quyết định có đặt bit fpsr.idc thành 1 hay không.

d12.2.88 “mvfr1_el1, aarch32 media and vfp feature register 1” cho biết rằng hỗ trợ ngoại lệ thực sự là hoàn toàn tùy chọn và cung cấp một cách để phát hiện xem có hỗ trợ không:

p>

fpftz, bit & lt; 3: 0 & gt;

Vào chế độ không. Chỉ ra rằng việc triển khai dấu phẩy động chỉ cung cấp hỗ trợ cho phương thức hoạt động rõ ràng. Giá trị được chỉ định là:

0b0000 Chưa hoàn thiện hoặc phần cứng chỉ hỗ trợ chế độ hoạt động rõ ràng.

0b0001 Phần cứng hỗ trợ các thuật toán không chuẩn hóa hoàn toàn.

Tất cả các giá trị khác đều được bảo lưu.

Trong armv8-a, các giá trị được phép là 0b0000 và 0b0001.

Điều này chỉ ra rằng việc triển khai chỉ hoàn nguyên về 0 khi điều hành con không được triển khai.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *