2015-09-23 17 views
5

Thư viện chuẩn C cung cấp round, lroundllround họat động chức năng trong C99. Tuy nhiên, các chức năng này không tuân thủ IEEE-754, bởi vì chúng không thực hiện "làm tròn của ngân hàng" một nửa-đến-ngay cả khi được uỷ quyền bởi IEEE. Half-to-even làm tròn yêu cầu kết quả được làm tròn đến giá trị thậm chí gần nhất nếu thành phần phân số chính xác là 0,5. Các tiêu chuẩn C99 thay vì ủy nhiệm một nửa-away-từ-zero như đã nêu trên cppreference.comIEEE-754 tuân thủ nửa vòng-to-even

1-3) Tính giá trị số nguyên gần nhất để arg (ở định dạng dấu chấm động), làm tròn trường hợp nửa chừng đi từ số không, không phụ thuộc chế độ làm tròn hiện tại.

Cách ad-hoc thường lệ để thực hiện làm tròn trong C là biểu thức (int)(x + 0.5f) đó, mặc dù là incorrect trong nghiêm ngặt IEEE-754 toán, thường được dịch bởi trình biên dịch vào đúng cvtss2si hướng dẫn. Tuy nhiên, điều này chắc chắn không phải là một giả định di động.

Làm cách nào tôi có thể triển khai một hàm sẽ làm tròn bất kỳ giá trị dấu phẩy động nào với ngữ nghĩa nửa đến thậm chí? Nếu có thể, hàm chỉ nên dựa vào ngôn ngữ và ngữ nghĩa thư viện chuẩn, để nó có thể hoạt động trên các loại dấu phẩy động không phải IEEE. Nếu điều này là không thể, một câu trả lời được xác định trong điều khoản của IEEE-754 bit đại diện cũng là chấp nhận được. Vui lòng mô tả bất kỳ hằng số nào theo số <limits.h> hoặc <limits>.

+0

Chúng tuân thủ tiêu chuẩn nhưng không thực hiện tất cả."Vòng tới gần nhất, quan hệ từ số không" là một trong các chế độ làm tròn IEEE-754. –

+2

IEEE-754 lưu ý rằng chế độ làm tròn sách giáo khoa tồn tại, nhưng chỉ dành cho các số thập phân. 'Việc thực hiện tiêu chuẩn này sẽ cung cấp roundTiesToEven và ba tham số làm tròn các thuộc tính. Việc triển khai định dạng thập phân của tiêu chuẩn này sẽ cung cấp roundTiesToAway làm thuộc tính làm tròn hướng đến người dùng có thể chọn . Thuộc tính làm tròn roundTiesToAway không cần thiết cho việc triển khai định dạng nhị phân . ’Làm tròn các mối quan hệ đến thậm chí là chế độ di động (và hợp lý) duy nhất. – 68ejxfcj5669

+3

Thay vì 'round()' nhìn vào 'rint()' được sử dụng với chế độ làm tròn mặc định là "tròn đến gần nhất hoặc thậm chí". – njuffa

Trả lời

5

Làm tròn số x và nếu chênh lệch giữa x và tròn (x) chính xác là +0.5 hoặc -0.5 và tròn (x) là lẻ, thì vòng (x) được làm tròn sai hướng, vì vậy bạn trừ sự khác biệt từ x.

+0

Có đảm bảo rằng '(tròn (x) - x)' sẽ trả về 0,5/-0,5 nếu thành phần phân số của x là chính xác một nửa, tức là đây là một hoạt động chính xác? – 68ejxfcj5669

+2

Có. Kết quả của 'vòng (x) - x' sẽ được thể hiện chính xác (giả sử định dạng IEEE 754). –

+1

Bí quyết xấu là sử dụng '2.0 * round (0.5 * x)' trong trường hợp 'fabs (x - round (x)) == 0.5', tiết kiệm nhiệm vụ hơi lộn xộn của việc tìm ra liệu 'tròn (x) 'là chẵn hoặc lẻ. –

2

Sử dụng remainder(double x, 1.0) từ thư viện chuẩn C. Điều này độc lập với chế độ làm tròn hiện tại.

Các chức năng còn lại tính toán thời gian còn lại x REM y theo yêu cầu của IEC 60559

remainder() là hữu ích ở đây như là đáp ứng mối quan hệ OP để thậm chí yêu cầu.


double round_to_nearest_ties_to_even(double x) { 
    x -= remainder(x, 1.0); 
    return x; 
} 

mã kiểm tra

void rtest(double x) { 
    double round_half_to_even = round_to_nearest_ties_to_even(x); 
    printf("x:%25.17le z:%25.17le \n", x, round_half_to_even); 
} 

void rtest3(double x) { 
    rtest(nextafter(x, -1.0/0.0)); 
    rtest(x); 
    rtest(nextafter(x, +1.0/0.0)); 
} 

int main(void) { 
    rtest3(-DBL_MAX); 
    rtest3(-2.0); 
    rtest3(-1.5); 
    rtest3(-1.0); 
    rtest3(-0.5); 
    rtest(nextafter(-0.0, -DBL_MAX)); 
    rtest(-0.0); 
    rtest(0.0); 
    rtest(nextafter(0.0, +DBL_MAX)); 
    rtest3(0.5); 
    rtest3(1.0); 
    rtest3(1.5); 
    rtest3(2.0); 
    rtest3(DBL_MAX); 
    rtest3(0.0/0.0); 
    return 0; 
} 

Output

x:      -inf z:      -inf 
x:-1.79769313486231571e+308 z:-1.79769313486231571e+308 
x:-1.79769313486231551e+308 z:-1.79769313486231551e+308 
x: -2.00000000000000044e+00 z: -2.00000000000000000e+00 
x: -2.00000000000000000e+00 z: -2.00000000000000000e+00 
x: -1.99999999999999978e+00 z: -2.00000000000000000e+00 
x: -1.50000000000000022e+00 z: -2.00000000000000000e+00 
x: -1.50000000000000000e+00 z: -2.00000000000000000e+00 tie to even 
x: -1.49999999999999978e+00 z: -1.00000000000000000e+00 
x: -1.00000000000000022e+00 z: -1.00000000000000000e+00 
x: -1.00000000000000000e+00 z: -1.00000000000000000e+00 
x: -9.99999999999999889e-01 z: -1.00000000000000000e+00 
x: -5.00000000000000111e-01 z: -1.00000000000000000e+00 
x: -5.00000000000000000e-01 z: 0.00000000000000000e+00 tie to even 
x: -4.99999999999999944e-01 z: 0.00000000000000000e+00 
x:-4.94065645841246544e-324 z: 0.00000000000000000e+00 
x: -0.00000000000000000e+00 z: 0.00000000000000000e+00 
x: 0.00000000000000000e+00 z: 0.00000000000000000e+00 
x: 4.94065645841246544e-324 z: 0.00000000000000000e+00 
x: 4.99999999999999944e-01 z: 0.00000000000000000e+00 
x: 5.00000000000000000e-01 z: 0.00000000000000000e+00 tie to even 
x: 5.00000000000000111e-01 z: 1.00000000000000000e+00 
x: 9.99999999999999889e-01 z: 1.00000000000000000e+00 
x: 1.00000000000000000e+00 z: 1.00000000000000000e+00 
x: 1.00000000000000022e+00 z: 1.00000000000000000e+00 
x: 1.49999999999999978e+00 z: 1.00000000000000000e+00 
x: 1.50000000000000000e+00 z: 2.00000000000000000e+00 tie to even 
x: 1.50000000000000022e+00 z: 2.00000000000000000e+00 
x: 1.99999999999999978e+00 z: 2.00000000000000000e+00 
x: 2.00000000000000000e+00 z: 2.00000000000000000e+00 
x: 2.00000000000000044e+00 z: 2.00000000000000000e+00 
x: 1.79769313486231551e+308 z: 1.79769313486231551e+308 
x: 1.79769313486231571e+308 z: 1.79769313486231571e+308 
x:      inf z:      inf 
x:      nan z:      nan 
x:      nan z:      nan 
x:      nan z:      nan 
5

Các thư viện chuẩn C cung cấp round, lroundllround gia đình của các chức năng trong C99. Tuy nhiên, các chức năng này không tuân thủ IEEE-754, bởi vì chúng không thực hiện "làm tròn ngân hàng" của một nửa đến thậm chí theo yêu cầu của IEEE ...

Nó không có ý nghĩa để nói về việc liệu hoặc không phải là một chức năng riêng lẻ là "tuân thủ IEEE-754". Việc tuân thủ IEEE-754 yêu cầu phải có một tập hợp các hoạt động loại dữ liệu với ngữ nghĩa xác định. Nó không yêu cầu các loại hoặc hoạt động đó có tên cụ thể, cũng không yêu cầu chỉ các hoạt động đó có sẵn. Việc triển khai có thể cung cấp bất kỳ chức năng bổ sung nào mà nó muốn và vẫn tuân thủ.Nếu thực hiện muốn cung cấp vòng-lẻ, tròn ngẫu nhiên, vòng-đi-từ-zero, và bẫy-nếu-inexact, nó có thể làm như vậy.

gì IEEE-754 thực sự đòi hỏi cho làm tròn là sáu hoạt động sau đây cung cấp:

convertToIntegerTiesToEven (x)

convertToIntegerTowardZero (x)

convertToIntegerTowardPositive (x)

convertToIntegerTowardNegative (x)

convertToIntegerTiesToAway (x)

convertToIntegerExact (x)

Trong C và C++, cuối cùng năm người các hoạt động này chắc chắn sẽ số trunc, ceil, floor, roundrint chức năng, tương ứng. C11 và C++ 14 không có ràng buộc cho lần đầu tiên, nhưng các bản sửa đổi trong tương lai sẽ sử dụng roundeven. Như bạn thấy, round thực sự là một trong những hoạt động cần thiết.

Tuy nhiên, roundeven không có sẵn trong việc triển khai hiện nay, trong đó đưa chúng ta đến phần tiếp theo của câu hỏi của bạn:

Cách ad-hoc thường lệ để thực hiện làm tròn trong C là biểu hiện (int)(x + 0.5f) đó, mặc dù là không chính xác trong toán học IEEE-754 nghiêm ngặt, thường được dịch bởi các trình biên dịch thành đúng hướng dẫn cvtss2si. Tuy nhiên, điều này chắc chắn không phải là một giả định di động.

Các vấn đề với biểu thức đó mở rộng vượt xa "toán học IEEE-754 nghiêm ngặt". Nó hoàn toàn không chính xác cho tiêu cực x, đưa ra câu trả lời sai cho nextDown(0.5) và chuyển tất cả các số nguyên lẻ trong 2 ** 23 binade thành số nguyên chẵn. Bất kỳ trình biên dịch dịch nó thành cvtss2si là khủng khiếp, khủng khiếp bị hỏng. Nếu bạn có một ví dụ về điều đó xảy ra, tôi rất thích nhìn thấy nó.

Làm cách nào tôi có thể triển khai một hàm sẽ làm tròn bất kỳ giá trị dấu phẩy động nào với ngữ nghĩa nửa đến thậm chí?

Như njuffa ghi nhận trong một chú thích, bạn có thể đảm bảo rằng các chế độ làm tròn mặc định được thiết lập và sử dụng rint (hoặc lrint, vì nó có vẻ như bạn thực sự muốn có một kết quả số nguyên), hoặc bạn có thể thực hiện riêng của bạn chức năng làm tròn bằng cách gọi round và sau đó sửa các trường hợp nửa chừng như gnasher729 gợi ý. Sau khi kết nối n1778 cho C được chấp nhận, bạn sẽ có thể sử dụng các chức năng roundeven hoặc fromfp để thực hiện thao tác này mà không cần phải kiểm soát chế độ làm tròn.

+0

Tôi đồng ý rằng (int) (x + 0.5f) là sai vì nhiều lý do. Tuy nhiên, MSVC đặc biệt phát hiện thành ngữ này và thay thế nó bằng cvtss2si, mà tôi đoán là tại sao nó thường được sử dụng. – 68ejxfcj5669

+0

@ 68ejxfcj5669: Tôi nghi ngờ đó là trường hợp, nhưng tôi rất muốn được chứng minh là sai. Tôi không có máy tính Windows để kiểm tra và tìm kiếm trên web không bật lên bất kỳ tài khoản nào của quá trình chuyển đổi này. Bạn có một ví dụ thực tế về một chương trình mà MSVC đã làm điều đó không? –

1

float loại dữ liệu có thể đại diện cho toàn bộ số, nhưng không có phân số, trong phạm vi 8388608.0f đến 16777216.0f. Bất kỳ số float nào lớn hơn 8388607.5f là số nguyên và không cần làm tròn. Thêm 8388608.0f vào bất kỳ số phi tiêu cực float nào nhỏ hơn số đó sẽ mang lại một số nguyên sẽ được làm tròn theo chế độ làm tròn hiện tại (thường là tròn nửa đến giờ). Trừ 8388608.0f sau đó sẽ tạo ra một phiên bản được làm tròn đúng của bản gốc (giả sử nó nằm trong một phạm vi phù hợp).

Như vậy, chúng ta có thể làm điều gì đó như:

float round(float f) 
{ 
    if (!(f > -8388608.0f && f < 8388608.0f)) // Return true for NaN 
    return f; 
    else if (f > 0) 
    return (float)(f+8388608.0f)-8388608.0f; 
    else 
    return (float)(f-8388608.0f)+8388608.0f; 
} 

và tận dụng các hành vi làm tròn tự nhiên bổ sung, mà không cần phải sử dụng bất kỳ khác "tròn-to-nguyên" cơ sở.

0

Sau đây là cách triển khai đơn giản nửa vòng để thậm chí chương trình theo tiêu chuẩn làm tròn của IEEE.

Logic: lỗi = 0,00001

  1. number = 2,5
  2. temp = sàn (2.5)% 2 = 2% 2 = 0
  3. x = -1 + temp = -1
  4. x * lỗi + số = 2.40009
  5. vòng (2,40009) = 2

Lưu ý: Lỗi ở đây là của 0,00001, ví dụ, nếu 2,500001 xảy ra, sau đó nó sẽ được làm tròn đến 2 thay vì 3

Python 2.7 thực hiện:

temp = (number)  
rounded_number = int(round(-1+ temp%2)*0.00001 + temp) 

Triển khai C++: (Sử dụng math.h cho chức năng sàn)

float temp = (number)  
int rounded_number = (int)((-1+ temp%2)*0.00001 + temp + 0.5) 

Kết quả sẽ cung cấp như sau. tiêu chuẩn:

(3,5) -> 4

(2,5) -> 2


Sửa 1: Như đã chỉ ra bởi @ Mark Dickinson trong các ý kiến. Lỗi có thể được sửa đổi theo yêu cầu trong mã của bạn để chuẩn hóa nó. Đối với python, để biến nó thành giá trị float nhỏ nhất có thể, bạn có thể làm như sau.

import sys 
error = sys.float_info.min 
+1

Nếu vòng 2.500001 đến 2 thay vì 3, thì đây không phải là vòng tuân thủ tiêu chuẩn. –

+0

Chương trình được viết cho người muốn thực hiện điều này trong mã của riêng họ. Lỗi có thể được thay đổi thành bất kỳ điểm chính xác nào cần thiết trong mã, tức là, nó thậm chí có thể là giá trị float nhỏ nhất có thể. 'error = sys.float_info.min' (cho Python), và tương đương với C++ –

+0

Được rồi, nhưng nó không trả lời câu hỏi: OP đang tìm kiếm hoạt động làm tròn tuân thủ IEEE 754. Mã trong câu trả lời này không đưa ra điều đó. Và việc sử dụng 'error = sys.float_info.min' không phải là một giải pháp. Nếu bạn kiểm tra mã của mình, bạn sẽ thấy rằng ngay cả mã hiện tại không cung cấp '4' từ đầu vào của' 3.5'; nó cho '3'. –

Các vấn đề liên quan