ค่าคู่ขั้นต่ำใน C / C ++


92

มีวิธีมาตรฐานและ / หรือแบบพกพาในการแทนค่าลบที่เล็กที่สุด (เช่นการใช้อินฟินิตี้เชิงลบ) ในโปรแกรม C (++) หรือไม่?

DBL_MIN ใน float.h เป็นจำนวนบวกที่น้อยที่สุด


4
ฉันจะไปที่ -DBL_MAX แต่ฉันแน่ใจว่ามีเหตุผลทางเทคนิคบางอย่างที่ทำไมถึงไม่เป็นเช่นนั้น :-)

4
@ นีลไม่มีไม่มีมันไม่เหมือน 2 จำนวนเต็มเสริม
fortran

ฉันยังไม่เห็นอะไรเลยในมาตรฐานที่บอกว่าช่วงของประเภทจุดลอยตัวจะต้องสมมาตรรอบศูนย์ แต่ค่าคงที่อยู่ในขีด จำกัด h และ <limits> แนะนำว่าทั้งมาตรฐาน C และ C ++ เป็นแบบที่พวกเขาคาดหวังไว้
Steve Jessop

4
จริงๆแล้ว DBL_MIN ใน float.h เป็นจำนวนมาตรฐานที่เป็นค่าบวกที่ น้อยที่สุด มีตัวเลขที่เล็กกว่าด้วยซ้ำ
fdermishin

1
@fortran: IEEE 754 FP ใช้บิตเครื่องหมายและฮาร์ดแวร์ FP ส่วนใหญ่ในทุกวันนี้คือ IEEE 754 แต่ C และ C ++ รองรับฮาร์ดแวร์ที่ไม่ใช่ IEEE 754 FP ดังนั้นคำถามจึงเปิดกว้างว่าภาษารับประกันได้หรือไม่ว่า -DBL_MAX ต้องเท่ากับค่าต่ำสุดที่แสดงได้
j_random_hacker

คำตอบ:


135

-DBL_MAX ใน ANSI Cซึ่งกำหนดไว้ใน float.h.


ดูเหมือนจะเป็นมาตรฐานและพกพาได้มากที่สุด
จะ

นี่คือคำอธิบายสำหรับ -1 ของฉัน: ใครหรืออะไรที่บอกว่า -DBL_MAX ได้รับการรับรองโดยภาษา C หรือ C ++ ว่าสามารถแสดงได้นับประสาอะไรกับค่าต่ำสุดที่แสดงได้ ข้อเท็จจริงที่ว่าฮาร์ดแวร์ FP ส่วนใหญ่เป็นไปตามมาตรฐาน IEEE 754 และใช้การแสดงนี้ไม่ได้หมายความว่า -DBL_MAX ได้รับการรับรองว่าจะทำงานบนแพลตฟอร์ม C ที่เป็นไปตามมาตรฐานใด ๆ
j_random_hacker

@j_random_hacker: ดูคำตอบของ Fortran 'ด้านล่าง'
JohnTortugo

3
@j_random_hacker นั่นเป็นจุดที่ดีมาก แต่มาตรฐาน C ต้องการ-DBL_MAXให้เป็นตัวแทนได้อย่างแน่นอนดังนั้นหากฮาร์ดแวร์ FP ไม่สามารถทำได้การใช้งานก็ต้องแก้ไข ดูแบบจำลองจุดลอยตัวใน5.2.4.2.2 ลักษณะของประเภทลอย <float.h> p2ของ C99 (อาจถูกย้ายไปที่อื่นตั้งแต่นั้นมา)

2
@j_random_hacker ใช่ แต่ p2 ระบุว่า e_min และ e_max ไม่ขึ้นอยู่กับบิตเครื่องหมายดังนั้นจึงDBL_MAXเท่ากับ (1 - b ^ −p) b ^ e_max ซึ่งแสดงได้อย่างแน่นอนค่า จำกัด ที่เป็นลบส่วนใหญ่จะเท่ากับ - (1 - b ^ −p) b ^ e_max และเนื่องจากเป็นเช่น-DBL_MAXนั้นการปฏิเสธDBL_MAXจึงไม่สามารถทำให้เกิดข้อผิดพลาดในการปัดเศษได้

70

ตัวเลขจุดลอยตัว (IEEE 754) เป็นแบบสมมาตรดังนั้นหากคุณสามารถแทนค่าที่ยิ่งใหญ่ที่สุด ( DBL_MAXหรือnumeric_limits<double>::max()) ได้ให้นำหน้าเครื่องหมายลบ

แล้วก็เป็นวิธีที่ยอดเยี่ยม:

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

6
+1 สำหรับชี้ความสมมาตรของเลขทศนิยม :)
Andrew Hare

4
แล้วการใช้งาน C / C ++ ที่ไม่ใช้ IEEE 754 จะเป็นอย่างไร
Steve Jessop

1
คู่มือสำหรับ -ffast-math ของ gcc ระบุว่า "Sets -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and -fcx-limited- range อ็อพชันนี้ไม่ได้เปิดใช้งานโดยอ็อพชัน -O ใด ๆ เนื่องจากอาจส่งผลให้เอาต์พุตของโปรแกรมไม่ถูกต้องซึ่งขึ้นอยู่กับการใช้งาน IEEE หรือ ISO กฎ / ข้อกำหนดสำหรับฟังก์ชันทางคณิตศาสตร์อย่างถูกต้องอย่างไรก็ตามอาจให้รหัสที่เร็วกว่าสำหรับโปรแกรมที่ทำ ไม่ต้องการการรับประกันตามข้อกำหนดเหล่านี้ " การคำนวณอย่างรวดเร็วเป็นการตั้งค่าทั่วไปและ Intel ICC เป็นต้นค่าเริ่มต้น สรุปแล้วไม่แน่ใจว่าสิ่งนี้หมายถึงอะไรสำหรับฉัน :-)
จะ

4
หมายความว่าการนำไปใช้ไม่ได้ใช้เลขคณิต IEEE 754 แต่เพื่อความเป็นธรรมตัวเลือกเหล่านี้ยังคงใช้การเป็นตัวแทนของ IEEE คุณอาจพบไลบรารีจำลองบางตัวที่ใช้การแสดงที่ไม่ใช่ IEEE เนื่องจากโปรเซสเซอร์บางตัวไม่ได้มีรูปแบบโฟลตดั้งเดิม (แม้ว่าจะเผยแพร่ C ABI ที่มีรูปแบบซึ่งสอดคล้องกับไลบรารีจำลองที่จัดหาโดยผู้ผลิตก็ตาม) ดังนั้นคอมไพเลอร์ทั้งหมดไม่สามารถใช้งานได้ ขึ้นอยู่กับสิ่งที่คุณหมายถึงเมื่อคุณขอ "มาตรฐานและ / หรือแบบพกพา" มีหลักการพกพาและแบบพกพาในทางปฏิบัติ
Steve Jessop

3
สิ่งที่คุณพูดเป็นจริงสำหรับ IEEE 754 แต่มาตรฐานไม่จำเป็นต้องใช้การเข้ารหัสนี้ (ตามที่ @SteveJessop ชี้ให้เห็นว่าแบบพกพาในทางปฏิบัติไม่เหมือนกับแบบพกพาในหลักการ)
Christophe

44

ในภาษา C ให้ใช้

#include <float.h>

const double lowest_double = -DBL_MAX;

ใน C ++ pre-11 ให้ใช้

#include <limits>

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

ใน C ++ 11 ขึ้นไปให้ใช้

#include <limits>

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

ไม่มีmin()ฟังก์ชั่นก่อน C ++ 11? หรือว่าเป็นมูลค่าที่แตกต่างจาก-max()? en.cppreference.com/w/cpp/types/numeric_limits
Alexis Wilke

5
@ อเล็กซิส: หากคุณดูที่สามแถวต่ำสุดในตารางบนหน้าที่คุณเชื่อมโยงคุณจะเห็นว่าminได้รับค่าบวกที่น้อยที่สุดในขนาดและlowestค่าลบที่ใหญ่ที่สุดในขนาด ใช่มันแย่มาก ยินดีต้อนรับสู่โลกที่ยอดเยี่ยมของ c ++ :-Pห้องสมุดมาตรฐาน
rubenvb

สำหรับ C ถูกกำหนดไว้ใน float.h. limits.hสำหรับจำนวนเต็ม
Ciprian Tomoiagă

33

ลองสิ่งนี้:

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

อ้างอิง: numeric_limits

คลาสนี้มีความเชี่ยวชาญสำหรับประเภทพื้นฐานแต่ละประเภทโดยสมาชิกจะส่งคืนหรือตั้งค่าเป็นค่าต่างๆที่กำหนดคุณสมบัติที่ประเภทมีในแพลตฟอร์มเฉพาะที่คอมไพล์


1
ทำไมไม่เพียง-numeric_limits<double>::max()?
k06a

4
@ k06a มีการปฏิเสธที่แสดงด้วยอักขระตัวเดียวในนิพจน์ที่ยาวเช่นนี้โดยที่สตริงระบุว่า "สูงสุด" จะได้รับคนไม่ช้าก็เร็ว ไม่ว่าจะเก็บไว้ในตัวแปรอธิบายหรือใช้-1 * ...เพื่อให้ชัดเจนขึ้น
Filip Haglund

20

คุณกำลังมองหาค่าอินฟินิตี้ที่แท้จริงหรือค่า จำกัด ขั้นต่ำ? หากเป็นแบบเดิมให้ใช้

-numeric_limits<double>::infinity()

ซึ่งใช้ได้เฉพาะเมื่อ

numeric_limits<double>::has_infinity

มิฉะนั้นคุณควรใช้

numeric_limits<double>::lowest()

ซึ่งแนะนำใน C ++ 11

หากlowest()ไม่สามารถใช้งานได้คุณสามารถถอยกลับไปที่

-numeric_limits<double>::max()

ซึ่งอาจแตกต่างจากโดยlowest()หลักการ แต่โดยปกติแล้วไม่ได้ใช้ในทางปฏิบัติ


+1 สำหรับความแตกต่างระหว่างมูลค่า จำกัด และมูลค่าไม่สิ้นสุด! แต่มาตรฐานไม่รับประกันการเข้ารหัสจุดลอยตัวแบบสมมาตร ดังนั้น-numeric_limits<double>::max()แม้ว่าจะใช้งานได้จริง แต่ก็ไม่สามารถพกพาได้อย่างสมบูรณ์ตามทฤษฎี
Christophe

@Christophe: [x] แก้ไข
Christoph

10

โซลูชัน C ++ แบบพกพาอย่างแท้จริง

จาก C ++ 11 คุณสามารถใช้ numeric_limits<double>::lowest(). ตามมาตรฐานจะส่งคืนสิ่งที่คุณต้องการ:

ค่า จำกัด x ดังกล่าวว่าไม่มีค่า Y จำกัด อื่น ๆ y < xที่ มีความหมายสำหรับเฉพาะทุกที่
is_bounded != false

การสาธิตออนไลน์


คำตอบ C ++ แบบไม่พกพามากมายที่นี่!

มีคำตอบมากมายเกิด-std::numeric_limits<double>::max()ขึ้น

โชคดีที่พวกเขาจะทำงานได้ดีในกรณีส่วนใหญ่ รูปแบบการเข้ารหัสจุดลอยตัวจะย่อยสลายตัวเลขในแมนทิสซาและเลขชี้กำลังและส่วนใหญ่ (เช่นIEEE-754 ที่เป็นที่นิยม) ใช้บิตเครื่องหมายที่แตกต่างกันซึ่งไม่ได้เป็นของแมนทิสซา สิ่งนี้ช่วยให้สามารถเปลี่ยนค่าบวกที่ใหญ่ที่สุดในค่าลบที่เล็กที่สุดเพียงแค่พลิกเครื่องหมาย:

ป้อนคำอธิบายภาพที่นี่

ทำไมไม่พกพาเหล่านี้

มาตรฐานไม่ได้กำหนดมาตรฐานจุดลอยตัวใด ๆ

ผมยอมรับว่าข้อโต้แย้งของฉันเป็นทฤษฎีนิด ๆ หน่อย ๆ แต่คิดว่าบางชงคอมไพเลอร์ excentric จะใช้การเข้ารหัสรูปแบบการปฏิวัติกับ mantissa เข้ารหัสในรูปแบบของบางส่วนเติมเต็มสอง การเข้ารหัสส่วนเสริมของ Two ไม่สมมาตร ตัวอย่างเช่นถ่าน 8 บิตที่ลงชื่อแล้วค่าบวกสูงสุดคือ 127 แต่ค่าลบต่ำสุดคือ -128 ดังนั้นเราจึงสามารถจินตนาการได้ว่าการเข้ารหัสจุดลอยตัวบางอย่างแสดงพฤติกรรมไม่สมมาตรที่คล้ายกัน

ฉันไม่ได้ตระหนักถึงการเข้ารหัสรูปแบบใด ๆ เช่นนั้น แต่ประเด็นก็คือว่ามาตรฐานไม่ได้รับประกันว่าพลิกป้ายถัวเฉลี่ยผลที่ตั้งใจไว้ ดังนั้นคำตอบยอดนิยมนี้ (ขออภัย!) ไม่สามารถถือเป็นโซลูชันมาตรฐานแบบพกพาได้อย่างสมบูรณ์! / * อย่างน้อยก็ไม่ใช่ถ้าคุณไม่ยืนยันว่าnumeric_limits<double>::is_iec559เป็นความจริง * /



1

คำถามเดิมเกี่ยวข้องกับความไม่มีที่สิ้นสุด ทำไมไม่ใช้

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

ตามนิยามของ IEEE? แน่นอนคุณสามารถลบล้างสิ่งนั้นได้


ความคิดดี ! และมันทำงาน แต่ถ้าnumeric_limits<double>::has_infinity && ! numeric_limits<double>::traps
Christophe

1

มีวิธีมาตรฐานและ / หรือแบบพกพาในการแทนค่าลบที่เล็กที่สุด (เช่นการใช้อินฟินิตี้เชิงลบ) ในโปรแกรม C (++) หรือไม่?

แนวทาง C

การใช้งานหลายคนสนับสนุน +/- อนันต์ดังนั้นเชิงลบมากที่สุดค่าdouble-INFINITY

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

มีแบบมาตรฐานและ / หรือแบบพกพา .... ?

ตอนนี้เราต้องพิจารณากรณีอื่น ๆ ด้วย:

  • ไม่มี infinities

เพียงแค่-DBL_MAX.

  • เฉพาะผู้ที่ไม่ได้ลงชื่ออินฟินิตี้

ผมคาดว่าในกรณีนี้ OP -DBL_MAXต้องการ

  • ค่า De DBL_MAXปกติมากขึ้นในขนาดกว่า

นี่เป็นกรณีที่ผิดปกติซึ่งน่าจะอยู่นอกความกังวลของ OP เมื่อdoubleใดที่ถูกเข้ารหัสเป็นคู่ของจุดลอยตัวเพื่อให้ได้ช่วง / ระยะถอยที่ต้องการ (ดูdouble-double ) จะมีค่าปกติ สูงสุดdoubleและอาจเป็นค่าปกติที่มากกว่า ฉันเคยเห็นการถกเถียงกันว่าDBL_MAXควรอ้างถึงสิ่งปกติที่ยิ่งใหญ่ที่สุดของสิ่งที่ยิ่งใหญ่ที่สุดของทั้งสองอย่าง

โชคดีที่วิธีการจับคู่นี้มักมี -infinity -INFINITYดังนั้นส่วนใหญ่ยังคงเป็นค่าลบ


เพื่อความสะดวกในการพกพามากขึ้นโค้ดสามารถลงไปตามเส้นทางได้

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

หากคุณไม่ได้เปิดใช้งานข้อยกเว้นการลอยตัว (ซึ่งคุณไม่ควรใช้) คุณสามารถพูดได้ง่ายๆว่า:

double neg_inf = -1/0.0;

สิ่งนี้ให้ผลลบอินฟินิตี้ หากคุณต้องการลูกลอยคุณสามารถส่งผลลัพธ์ได้

float neg_inf = (float)-1/0.0;

หรือใช้เลขคณิตที่มีความแม่นยำเดียว

float neg_inf = -1.0f/0.0f;

ผลลัพธ์จะเหมือนกันเสมอมีการแทนค่าอินฟินิตี้เชิงลบเพียงค่าเดียวในทั้งความแม่นยำเดี่ยวและคู่และจะแปลงซึ่งกันและกันตามที่คุณคาดหวัง


ทำไมคุณถึงทำสิ่งนี้แทนที่จะเขียนเพียง-INFINITY
MM

นอกจากนี้อินฟินิตี้อาจมีหรือไม่มีอยู่และถ้ามีอยู่ผลบวกและลบอาจไม่สามารถแยกแยะได้ (ในมาตรฐาน C)
MM

ในคอมไพเลอร์และ / หรือสถาปัตยกรรมจำนวนมากโค้ด C / C ++ ของคุณจะทำให้คุณเผยแพร่ค่าอินฟินิตี้และ NaN ได้ช้าลง
markgalassi

@markgalassi โปรดใช้เวลามองใกล้: คุณจะสังเกตได้ว่าneg_infจะเริ่มต้นกับค่าคงที่ คอมไพเลอร์จะดูแลการคำนวณinfค่า และเมื่อคุณใช้เป็นค่า null ในการคำนวณค่าสูงสุดการวนซ้ำครั้งแรกโดยทั่วไปจะเขียนทับด้วยค่าที่มากกว่า คือประสิทธิภาพแทบจะไม่มีปัญหา และ OP ถามเป็นพิเศษเกี่ยวกับ "เช่นการใช้อินฟินิตี้เชิงลบ" และ-infเป็นคำตอบเดียวที่ถูกต้องสำหรับเรื่องนี้ คุณได้ลดคะแนนคำตอบที่ถูกต้องและมีประโยชน์
cmaster - คืนสถานะโมนิกา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.