giá trị nhân đôi tối thiểu trong C / C ++


92

Có cách tiêu chuẩn và / hoặc di động nào để biểu diễn giá trị âm nhỏ nhất (ví dụ: sử dụng âm vô cực) trong chương trình C (++) không?

DBL_MIN trong float.h là số dương nhỏ nhất .


4
Tôi sẽ đi cho -DBL_MAX, nhưng tôi chắc chắn rằng có một số lý do kỹ thuật tại sao điều này không phải là quá :-)

4
@Neil, không có, nó không giống như 2 số nguyên Bổ sung
fortran

Tôi chưa thấy bất cứ điều gì trong tiêu chuẩn để nói rằng phạm vi của các loại dấu phẩy động phải đối xứng xung quanh số không. Nhưng các hằng số trong limit.h và <limits> gợi ý rằng cả tiêu chuẩn C và C ++ đều mong đợi chúng sẽ như vậy.
Steve Jessop,

4
Trên thực tế DBL_MIN trong float.h là số chuẩn hóa dương nhỏ nhất . Có những con số thậm chí còn nhỏ hơn.
fdermishin

1
@fortran: IEEE 754 FP sử dụng bit dấu và chắc chắn hầu hết phần cứng FP ngày nay là IEEE 754. Nhưng C và C ++ hỗ trợ phần cứng không phải IEEE 754 FP, vì vậy câu hỏi được đặt ra là liệu ngôn ngữ có đảm bảo rằng -DBL_MAX phải bằng giá trị nhỏ nhất có thể biểu diễn.
j_random_hacker

Câu trả lời:


135

-DBL_MAX trong ANSI C , được định nghĩa trong float.h.


cái này có vẻ là tiêu chuẩn và di động nhất
Sẽ có

Đây là lời giải thích cho -1 của tôi: ai hoặc điều gì nói rằng -DBL_MAX được ngôn ngữ C hoặc C ++ đảm bảo là có thể biểu diễn được, hãy để một mình giá trị tối thiểu có thể biểu diễn? Thực tế là hầu hết phần cứng FP đều tuân theo IEEE 754 và nó sử dụng cách biểu diễn này, không có nghĩa là -DBL_MAX được đảm bảo hoạt động trên bất kỳ nền tảng C tuân thủ tiêu chuẩn nào.
j_random_hacker

@j_random_hacker: xem câu trả lời của fortran 'bên dưới'.
JohnTortugo

3
@j_random_hacker Đó là một điểm rất tốt, nhưng tiêu chuẩn C yêu cầu -DBL_MAXphải có khả năng biểu diễn chính xác, vì vậy nếu phần cứng FP không có khả năng đó, thì việc triển khai chỉ phải làm việc xung quanh nó. Xem mô hình dấu phẩy động trong 5.2.4.2.2 Đặc điểm của các kiểu dấu phẩy động <float.h> p2 của C99 (có thể đã được chuyển đi nơi khác kể từ đó).

2
@j_random_hacker Có, nhưng p2 chỉ định e_min và e_max độc lập với bit dấu, vì vậy DBL_MAXchính xác là (1 - b ^ −p) b ^ e_max, có thể biểu diễn chính xác, giá trị hữu hạn âm nhất chính xác là - (1 - b ^ −p) b ^ e_max, và vì điều đó xảy ra chính xác -DBL_MAXnên việc phủ định DBL_MAXcũng không thể tạo ra bất kỳ lỗi làm tròn nào.

70

Các số dấu phẩy động (IEEE 754) là đối xứng, vì vậy nếu bạn có thể đại diện cho giá trị lớn nhất ( DBL_MAXhoặc numeric_limits<double>::max()), chỉ cần thêm một dấu trừ.

Và sau đó là cách tuyệt vời:

double f;
(*((long long*)&f))= ~(1LL<<52);

6
1 Đối với chỉ ra tính đối xứng của các số dấu chấm động :)
Andrew Hare

4
Điều gì về triển khai C / C ++ không sử dụng IEEE 754 float?
Steve Jessop,

1
hướng dẫn sử dụng của gcc cho -ffast-math cho biết "Sets -fno-math-errno, -funsafe-math-Optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signal-nans and -fcx-limited- phạm vi Tùy chọn này không được bật bởi bất kỳ tùy chọn -O nào vì nó có thể dẫn đến kết quả đầu ra không chính xác cho các chương trình phụ thuộc vào việc triển khai chính xác các quy tắc / thông số kỹ thuật IEEE hoặc ISO cho các hàm toán học. Tuy nhiên, nó có thể mang lại mã nhanh hơn cho các chương trình thực hiện không yêu cầu đảm bảo các thông số kỹ thuật này. " Phép toán nhanh là một cài đặt phổ biến và Intel ICC chẳng hạn sẽ đặt mặc định cho nó. Nói chung, không chắc điều này có ý nghĩa gì với tôi :-)
Sẽ có

4
Nó có nghĩa là các triển khai không sử dụng số học IEEE 754, nhưng công bằng mà nói, các tùy chọn đó vẫn sử dụng biểu diễn IEEE. Bạn có thể tìm thấy một số thư viện mô phỏng sử dụng đại diện không phải IEEE, vì không phải tất cả các bộ xử lý đều có định dạng float gốc (mặc dù chúng có thể xuất bản C ABI bao gồm một định dạng, tương ứng với các lib mô phỏng do nhà sản xuất cung cấp). Do đó không phải tất cả các trình biên dịch đều có thể sử dụng một. Chỉ phụ thuộc vào ý của bạn khi bạn hỏi "tiêu chuẩn và / hoặc xách tay", về nguyên tắc thì có thể xách tay và di động trên thực tế.
Steve Jessop,

3
Những gì bạn nói là đúng với IEEE 754, nhưng tiêu chuẩn không yêu cầu sử dụng mã hóa này (như @SteveJessop đã chỉ ra, trên thực tế di động không giống như di động về nguyên tắc).
Christophe

44

Trong C, sử dụng

#include <float.h>

const double lowest_double = -DBL_MAX;

Trong C ++ trước 11, sử dụng

#include <limits>

const double lowest_double = -std::numeric_limits<double>::max();

Trong C ++ 11 trở đi, sử dụng

#include <limits>

constexpr double lowest_double = std::numeric_limits<double>::lowest();

Không phải là min()chức năng có sẵn trước khi C ++ 11? Hay đó là một giá trị khác -max()? en.cppreference.com/w/cpp/types/numeric_limits
Alexis Wilke

5
@Alexis: nếu bạn nhìn vào ba hàng thấp nhất trong bảng trên trang mà bạn đã liên kết, bạn sẽ thấy rằng mingiá trị dương nhỏ nhất về độ lớn và lowestgiá trị âm lớn nhất về độ lớn. Vâng, thật là khủng khiếp. Chào mừng đến với thế giới rực rỡ của thư viện chuẩn C ++ :-P.
rubenvb

đối với C nó được định nghĩa trong float.h. limits.hdành cho số nguyên
Ciprian Tomoiagă

33

Thử đi:

-1 * numeric_limits<double>::max()

Tài liệu tham khảo: numeric_limits

Lớp này chuyên biệt cho từng kiểu cơ bản, với các thành viên của nó trả về hoặc đặt thành các giá trị khác nhau xác định các thuộc tính của kiểu đó trong nền tảng cụ thể mà nó biên dịch.


1
Tại sao không chỉ -numeric_limits<double>::max()?
k06a,

4
@ k06a có phủ định được đại diện bởi một ký tự duy nhất trong một biểu thức dài như vậy, trong đó chuỗi thậm chí cho biết "tối đa", chắc chắn sớm hay muộn sẽ có ai đó. Nó được lưu trữ trong một biến mô tả hoặc sử dụng -1 * ...để làm cho nó rõ ràng hơn một chút.
Filip Haglund

20

Bạn đang tìm kiếm giá trị vô hạn thực tế hoặc giá trị hữu hạn tối thiểu? Nếu trước đây, hãy sử dụng

-numeric_limits<double>::infinity()

chỉ hoạt động nếu

numeric_limits<double>::has_infinity

Nếu không, bạn nên sử dụng

numeric_limits<double>::lowest()

đã được giới thiệu trong C ++ 11.

Nếu lowest()không có sẵn, bạn có thể quay lại

-numeric_limits<double>::max()

điều này có thể khác lowest()về nguyên tắc, nhưng thường không khác trong thực tế.


+1 cho sự khác biệt giữa giá trị hữu hạn và vô hạn! Nhưng tiêu chuẩn này không đảm bảo mã hóa dấu chấm động đối xứng. Vì vậy, -numeric_limits<double>::max()ngay cả khi nó hoạt động trong thực tế cũng không hoàn toàn có thể di chuyển được trên lý thuyết.
Christophe

@Christophe: [x] đã sửa
Christoph

10

Một giải pháp C ++ thực sự di động

Như từ C ++ 11, bạn có thể sử dụng numeric_limits<double>::lowest(). Theo tiêu chuẩn, nó trả về chính xác những gì bạn đang tìm kiếm:

Một giá trị hữu hạn x sao cho không có giá trị hữu hạn nào khác y ở đâu y < x.
Có ý nghĩa cho tất cả các chuyên ngành trong đó is_bounded != false.

Bản demo trực tuyến


Rất nhiều câu trả lời C ++ không di động ở đây!

Có rất nhiều câu trả lời cho -std::numeric_limits<double>::max().

May mắn thay, chúng sẽ hoạt động tốt trong hầu hết các trường hợp. Các lược đồ mã hóa dấu chấm động phân tách một số trong phần định trị và số mũ và hầu hết chúng (ví dụ như IEEE-754 phổ biến ) sử dụng một bit dấu riêng biệt, không thuộc về phần định trị. Điều này cho phép biến đổi dương lớn nhất thành âm nhỏ nhất chỉ bằng cách lật dấu:

nhập mô tả hình ảnh ở đây

Tại sao những thứ này không di động?

Tiêu chuẩn không áp đặt bất kỳ tiêu chuẩn dấu phẩy động nào.

Tôi đồng ý rằng lập luận của tôi hơi mang tính lý thuyết, nhưng giả sử rằng một nhà sản xuất trình biên dịch chuyên nghiệp nào đó sẽ sử dụng một lược đồ mã hóa mang tính cách mạng với phần định trị được mã hóa trong một số biến thể của phần bổ sung của hai . Mã hóa bổ sung của hai không đối xứng. ví dụ đối với một char 8 bit có dấu, giá trị dương tối đa là 127, nhưng giá trị âm tối thiểu là -128. Vì vậy, chúng ta có thể tưởng tượng một số mã hóa dấu phẩy động hiển thị hành vi bất đối xứng tương tự.

Tôi không biết về bất kỳ lược đồ mã hóa nào như vậy, nhưng vấn đề là tiêu chuẩn không đảm bảo rằng việc lật dấu hiệu mang lại kết quả dự kiến . Vì vậy, câu trả lời phổ biến này (xin lỗi các bạn!) Không thể được coi là giải pháp tiêu chuẩn di động hoàn toàn! / * ít nhất là không nếu bạn không khẳng định điều đó numeric_limits<double>::is_iec559là đúng * /



1

Câu hỏi ban đầu liên quan đến vô hạn. Vì vậy, tại sao không sử dụng

#define Infinity  ((double)(42 / 0.0))

theo định nghĩa của IEEE? Bạn có thể phủ nhận điều đó tất nhiên.


Ý kiến ​​hay ! Và nó hoạt động . Nhưng chỉ khinumeric_limits<double>::has_infinity && ! numeric_limits<double>::traps
Christophe

1

Có cách tiêu chuẩn và / hoặc di động nào để biểu diễn giá trị âm nhỏ nhất (ví dụ: sử dụng âm vô cực) trong chương trình C (++) không?

C cách tiếp cận.

Nhiều triển khai hỗ trợ +/- infinities, vì vậy doublegiá trị âm nhất là -INFINITY.

#include <math.h>
double most_negative = -INFINITY;

Có một cách tiêu chuẩn và / hoặc di động ....?

Bây giờ chúng ta cũng cần xem xét các trường hợp khác:

  • Không có vô hạn

Đơn giản -DBL_MAXthôi.

  • Chỉ một vô cực không dấu .

Tôi mong đợi trong trường hợp này, OP sẽ thích hơn -DBL_MAX.

  • Giá trị giảm bình thường có độ lớn lớn hơn DBL_MAX.

Đây là một trường hợp bất thường, có khả năng nằm ngoài mối quan tâm của OP. Khi doubleđược mã hóa dưới dạng một cặp dấu phẩy động để đạt được phạm vi / tuế sai mong muốn, (xem kép kép ) tồn tại giá trị bình thường tối đa doublevà có lẽ là giá trị bình thường lớn hơn . Tôi đã thấy cuộc tranh luận nếu DBL_MAXnên tham khảo điều bình thường lớn nhất , điều nhất của cả hai.

May mắn thay, cách tiếp cận được ghép nối này thường bao gồm-vô cùng, vì vậy giá trị âm nhất vẫn còn -INFINITY.


Để có thêm tính di động, mã có thể đi xuống tuyến đường

// HUGE_VAL is designed to be infinity or DBL_MAX (when infinites are not implemented)
// .. yet is problematic with unsigned infinity.
double most_negative1 = -HUGE_VAL;  

// Fairly portable, unless system does not understand "INF"
double most_negative2 = strtod("-INF", (char **) NULL);

// Pragmatic
double most_negative3 = strtod("-1.0e999999999", (char **) NULL);

// Somewhat time-consuming
double most_negative4 = pow(-DBL_MAX, 0xFFFF /* odd value */);

// My suggestion
double most_negative5 = (-DBL_MAX)*DBL_MAX;

-1

Nếu bạn chưa bật ngoại lệ float (mà bạn không nên imho), bạn chỉ cần nói:

double neg_inf = -1/0.0;

Điều này mang lại âm vô cùng. Nếu bạn cần một phao, bạn có thể truyền kết quả

float neg_inf = (float)-1/0.0;

hoặc sử dụng số học chính xác đơn

float neg_inf = -1.0f/0.0f;

Kết quả luôn giống nhau, có chính xác một đại diện của âm vô cực ở cả độ chính xác đơn và kép, và chúng chuyển đổi cho nhau như bạn mong đợi.


Tại sao bạn lại làm điều này thay vì chỉ viết-INFINITY
MM

Ngoài ra, vô cực có thể tồn tại hoặc không, và nếu nó tồn tại thì dương và âm có thể không phân biệt được (trong Tiêu chuẩn C).
MM

Trong nhiều trình biên dịch và / hoặc kiến ​​trúc, mã C / C ++ của bạn sẽ làm chậm nhiều khi bạn truyền giá trị vô cực và NaN.
markgalassi

@markgalassi Vui lòng xem kỹ hơn: Bạn sẽ nhận thấy rằng nó neg_infđược khởi tạo thành một giá trị không đổi . Trình biên dịch sẽ đảm nhận việc tính toán infgiá trị. Và khi bạn sử dụng nó dưới dạng null-value để tính toán giá trị tối đa, lần lặp đầu tiên thường sẽ ghi đè nó bằng một giá trị lớn hơn. Tức là hiệu suất hầu như không có vấn đề gì. Và OP hỏi cụ thể về "ví dụ: sử dụng âm vô cực", và -infthực sự là câu trả lời chính xác duy nhất cho điều này. Bạn đã phản đối một câu trả lời đúng và hữu ích.
cmaster - phục hồi monica
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.