วิธีใช้ nan และ inf ใน C?


91

ฉันมีวิธีการเชิงตัวเลขที่สามารถคืนค่า nan หรือ inf ได้หากมีข้อผิดพลาดและสำหรับการทดสอบฉันต้องการบังคับให้ส่งคืน nan หรือ inf ชั่วคราวเพื่อให้แน่ใจว่าสถานการณ์ได้รับการจัดการอย่างถูกต้อง มีวิธีที่เชื่อถือได้และไม่ขึ้นกับคอมไพเลอร์ในการสร้างค่าของ nan และ inf ใน C หรือไม่?

หลังจาก googling ประมาณ 10 นาทีฉันสามารถค้นหาโซลูชันที่ขึ้นกับคอมไพเลอร์ได้เท่านั้น


ลอยไม่ได้กำหนดโดยมาตรฐาน C ดังนั้นจึงไม่มีวิธีที่ไม่ขึ้นกับคอมไพเลอร์ในการทำสิ่งที่คุณต้องการ
Johan Kotlinski

คำตอบ:


87

คุณสามารถทดสอบว่าการใช้งานของคุณมีหรือไม่:

การมีอยู่ของINFINITYรับประกันโดย C99 (หรืออย่างน้อยที่สุดแบบร่างล่าสุด) และ "ขยายเป็นนิพจน์คงที่ของประเภท float ที่แสดงค่าอินฟินิตี้ที่เป็นบวกหรือไม่ได้ลงนามถ้ามีมิฉะนั้นจะเป็นค่าคงที่บวกของประเภทโฟลตที่ล้นในเวลาแปล"

NAN อาจกำหนดหรือไม่ก็ได้และ "ถูกกำหนดก็ต่อเมื่อการใช้งานรองรับ NaN แบบเงียบสำหรับประเภทโฟลตมันจะขยายเป็นนิพจน์คงที่ของประเภทโฟลตที่แสดงถึง NaN ที่เงียบ"

โปรดทราบว่าหากคุณกำลังเปรียบเทียบค่าทศนิยมให้ทำดังนี้

ถึงอย่างนั้น

เป็นเท็จ วิธีหนึ่งในการตรวจสอบ NaN คือ:

คุณยังสามารถทำได้: a != aเพื่อทดสอบว่าaเป็น NaN หรือไม่

นอกจากนี้ยังมีisfinite(), isinf(), isnormal()และsignbit()แมโครในmath.hใน C99

C99 ยังมีnanฟังก์ชั่น:

(อ้างอิง: n1256).

เอกสาร INFINITY เอกสาร NAN


2
คำตอบที่ยอดเยี่ยม การอ้างอิงสำหรับมาโคร NAN และ INFINITY คือ C99 §7.12วรรค 4 และ 5 นอกจากนี้ (isnan (a)) คุณยังสามารถตรวจสอบ NaN โดยใช้ (a! = a) ในการใช้งาน C.
Stephen Canon

24
สำหรับคนรักการอ่านไม่a != aควรใช้
Chris Kerekes

1
@ChrisKerekes: น่าเศร้าที่พวกเราบางคนมี NAN แต่ไม่ใช่ isnan () ใช่นี้เป็น 2017 :(
เอฟเอฟ

C ไม่ต้องการเมื่อaใดที่ไม่ใช่ตัวเลขa == NANเพื่อส่งคืนเท็จ IEEE ต้องการ แม้การใช้งานที่เป็นไปตามมาตรฐาน IEEE ทำเช่นนั้นส่วนใหญ่ เมื่อไม่ได้ดำเนินการยังคงดีกว่าที่จะตัดการทดสอบกว่ารหัสโดยตรงisnan() a == NAN
chux - คืนสถานะ Monica

34

ไม่มีวิธีที่เป็นอิสระของคอมไพเลอร์ในการทำเช่นนี้เนื่องจากมาตรฐาน C (หรือ C ++) ไม่ได้กล่าวว่าประเภทคณิตศาสตร์ทศนิยมต้องรองรับ NAN หรือ INF

แก้ไข:ฉันเพิ่งตรวจสอบถ้อยคำของมาตรฐาน C ++ และมันบอกว่าฟังก์ชั่นเหล่านี้ (สมาชิกของคลาส templated numeric_limits):

จะส่งคืนการเป็นตัวแทน NAN "ถ้ามี" มันไม่ได้ขยายความว่า "ถ้ามี" หมายถึงอะไร แต่น่าจะมีบางอย่างเช่น "ถ้าตัวแทน FP ของการใช้งานสนับสนุน ในทำนองเดียวกันมีฟังก์ชั่น:

ซึ่งส่งกลับตัวแทน INF เชิงบวก "ถ้ามี"

ทั้งสองอย่างนี้กำหนดไว้ใน<limits>ส่วนหัว - ฉันเดาว่ามาตรฐาน C มีบางอย่างที่คล้ายกัน (อาจเป็น "ถ้ามี") แต่ฉันไม่มีสำเนาของมาตรฐาน C99 ปัจจุบัน


ที่น่าผิดหวังและน่าประหลาดใจ C และ C ++ ไม่สอดคล้องกับตัวเลขทศนิยมของ IEEE ซึ่งมีการแทนค่ามาตรฐานสำหรับ nan และ inf หรือไม่
กราฟิก Noob

14
ใน C99, ส่วนหัว C <math.h>กำหนดnan(), nanf()และnanl()ผลตอบแทนที่การแสดงที่แตกต่างกันของน่าน (เป็นdouble, floatและintตามลำดับ) และอินฟินิตี้ (ถ้าใช้ได้) อาจจะกลับมาโดยการสร้างเป็นหนึ่งเดียวกับlog(0)หรือบางสิ่งบางอย่าง ไม่มีวิธีมาตรฐานในการตรวจสอบแม้แต่ใน C99 <float.h>ส่วนหัว ( <limits.h>สำหรับประเภทหนึ่ง) เป็นที่น่าเสียดายที่เงียบเกี่ยวกับinfและnanค่า
Chris Lutz

ว้าวนั่นเป็นการผสมผสานครั้งใหญ่ nanl()ส่งกลับ a long doubleไม่ใช่intอย่างที่ความคิดเห็นของฉันพูด ฉันไม่รู้ว่าทำไมฉันถึงไม่รู้ตัวตอนที่กำลังพิมพ์อยู่
Chris Lutz

@ คริสดูคำตอบของฉันสำหรับ C99
Alok Singhal

2
@IngeHenriksen - ค่อนข้างแน่ใจว่า Microsoft ระบุว่าไม่มีเจตนาให้ VC ++ รองรับ C99
Chris Lutz

25

สิ่งนี้ใช้ได้กับทั้งfloatและdouble:

แก้ไข: ตามที่มีคนพูดไปแล้วมาตรฐาน IEEE เก่ากล่าวว่าค่าดังกล่าวควรเพิ่มกับดัก แต่คอมไพเลอร์ใหม่มักจะปิดกับดักและส่งคืนค่าที่กำหนดเนื่องจากการดักรบกวนขัดขวางการจัดการข้อผิดพลาด


การดักจับเป็นทางเลือกหนึ่งสำหรับการจัดการข้อผิดพลาดที่อนุญาตภายใต้ 754-1985 พฤติกรรมที่ใช้โดยฮาร์ดแวร์ / คอมไพเลอร์สมัยใหม่ส่วนใหญ่ยังได้รับอนุญาต (และเป็นพฤติกรรมที่ต้องการสำหรับสมาชิกหลายคนในคณะกรรมการ) ผู้ใช้งานหลายคนสันนิษฐานอย่างไม่ถูกต้องว่าจำเป็นต้องมีการวางกับดักเนื่องจากโชคไม่ดีที่ใช้คำว่า "ข้อยกเว้น" ในมาตรฐาน สิ่งนี้ได้รับการชี้แจงอย่างมากในฉบับปรับปรุง 754-2008
Stephen Canon

สวัสดีสตีเฟนคุณพูดถูก แต่มาตรฐานยังระบุว่า: "ผู้ใช้ควรสามารถขอกับดักในข้อยกเว้นห้าข้อใดก็ได้โดยระบุตัวจัดการสำหรับมันเขาควรจะสามารถขอให้ปิดใช้ตัวจัดการที่มีอยู่ได้ บันทึกหรือกู้คืนนอกจากนี้เขาควรจะสามารถระบุได้ว่ามีการเปิดใช้งานตัวจัดการกับดักเฉพาะสำหรับข้อยกเว้นที่กำหนดไว้หรือไม่ " "ควร" ตามที่กำหนดไว้ (2. คำจำกัดความ) หมายถึง "แนะนำอย่างยิ่ง" และควรนำไปใช้งานต่อเมื่อสถาปัตยกรรม ฯลฯ ทำให้ไม่สามารถใช้งานได้จริง 80x86 รองรับมาตรฐานอย่างสมบูรณ์ดังนั้นจึงไม่มีเหตุผลใดที่ C จะไม่รองรับ
Thorsten S.

ฉันยอมรับว่า C ควรต้องการจุดลอยตัว 754 (2008) แต่มีเหตุผลที่ดีที่จะไม่ทำเช่นนั้น โดยเฉพาะ C ใช้ในสภาพแวดล้อมทุกประเภทที่นอกเหนือจาก x86 รวมถึงอุปกรณ์ฝังตัวที่ไม่มีจุดลอยตัวของฮาร์ดแวร์และอุปกรณ์ประมวลผลสัญญาณที่โปรแกรมเมอร์ไม่ต้องการใช้จุดลอยตัว ถูกหรือผิดการใช้งานเหล่านี้ก่อให้เกิดความเฉื่อยจำนวนมากในข้อกำหนดภาษา
Stephen Canon

ฉันไม่รู้ว่าทำไมคำตอบยอดนิยมถึงเกิดขึ้นที่นั่น ไม่ได้ให้วิธีใด ๆ ในการสร้างค่าที่ร้องขอ คำตอบนี้ไม่
drysdam

#define is_nan(x) ((x) != (x))อาจเป็นประโยชน์ในการทดสอบ NAN แบบง่ายๆ
Bob Stein

21

วิธีที่เป็นอิสระของคอมไพเลอร์ แต่ไม่ใช่วิธีที่เป็นอิสระของโปรเซสเซอร์เพื่อรับสิ่งเหล่านี้:

สิ่งนี้ควรใช้ได้กับโปรเซสเซอร์ใด ๆ ที่ใช้รูปแบบทศนิยม IEEE 754 (ซึ่ง x86 ทำ)

UPDATE: ทดสอบและอัปเดต


2
@WaffleMatt - ทำไมพอร์ตนี้ถึงไม่อยู่ระหว่าง 32/64 บิต? IEEE 754 single-precision float คือ 32 บิตโดยไม่คำนึงถึงขนาดที่อยู่ของโปรเซสเซอร์ที่อยู่ข้างใต้
Aaron

6
หล่อไปยัง(float &)? นั่นดูไม่เหมือน C สำหรับฉัน คุณต้องการint i = 0x7F800000; return *(float *)&i;
Chris Lutz

6
โปรดทราบว่า0x7f800001เป็นสิ่งที่เรียกว่าNaN การส่งสัญญาณในมาตรฐาน IEEE-754 แม้ว่าไลบรารีและฮาร์ดแวร์ส่วนใหญ่จะไม่รองรับการส่งสัญญาณ NaN แต่ก็น่าจะดีกว่าที่จะคืนค่า NaN แบบเงียบ0x7fc00000
Stephen Canon

6
คำเตือน:สิ่งนี้อาจทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดผ่านการละเมิดกฎการใช้นามแฝงที่เข้มงวด แนะนำ (และที่ดีที่สุดได้รับการสนับสนุนในการคอมไพเลอร์) วิธีการทำประเภทเล่นสำนวนผ่านสมาชิกสหภาพแรงงาน
ulidtko

2
นอกเหนือจากปัญหานามแฝงที่เข้มงวดที่ @ulidtko ชี้ให้เห็นแล้วสิ่งนี้ถือว่าเป้าหมายใช้ endian เดียวกันสำหรับจำนวนเต็มเป็นทศนิยมซึ่งไม่ได้เป็นเช่นนั้นเสมอไป
mr.stobbe

16

4
นี่คือโซลูชันแบบพกพาอันชาญฉลาด! C99 ต้องการstrtodและแปลง NaN และ Inf
ulidtko

1
ไม่ใช่ว่ามีข้อเสียเปรียบในการแก้ปัญหานี้ พวกมันไม่ใช่ค่าคงที่ คุณไม่สามารถใช้ค่าเหล่านี้เพื่อเริ่มต้นตัวแปรส่วนกลางเช่น (หรือเพื่อเริ่มต้นอาร์เรย์)
Marc

1
@ มาร์ค. คุณสามารถมีฟังก์ชัน initializer ที่เรียกใช้ครั้งเดียวและตั้งค่าในเนมสเปซส่วนกลางได้ตลอดเวลา เป็นข้อเสียเปรียบที่ใช้การได้มาก
Mad Physicist

3

และ


0

ฉันแปลกใจเหมือนกันที่ไม่รวบรวมค่าคงที่ของเวลา แต่ฉันคิดว่าคุณสามารถสร้างค่าเหล่านี้ได้ง่ายพอเพียงแค่ดำเนินการตามคำสั่งที่ส่งคืนผลลัพธ์ที่ไม่ถูกต้อง หารด้วย 0, ล็อกของ 0, แทนของ 90, แบบนั้น


0

ฉันมักจะใช้

หรือ

ซึ่งผลงานอย่างน้อยใน IEEE 754 บริบทเพราะค่า double 1e308แทนได้สูงสุดคือประมาณ 1e309จะทำงานได้ดีเช่นเดียวกับที่ควร1e99999แต่สามเก้าก็เพียงพอและน่าจดจำ เนื่องจากนี่เป็นทั้งลิเทอรัลคู่ (ใน#defineกรณีนี้) หรือInfค่าจริงค่านี้จะยังคงไม่สิ้นสุดแม้ว่าคุณจะใช้การลอยตัวแบบ 128 บิต ("long double")


1
สิ่งนี้อันตรายมากในความคิดของฉัน ลองนึกภาพว่ามีคนย้ายรหัสของคุณไปเป็น 128 บิตลอยตัวได้อย่างไรใน 20 ปีหรือมากกว่านั้น (หลังจากรหัสของคุณผ่านวิวัฒนาการที่ซับซ้อนอย่างไม่น่าเชื่อไม่มีขั้นตอนใดที่คุณสามารถคาดเดาได้ในปัจจุบัน) ทันใดนั้นช่วงสัญลักษณ์อย่างมากเพิ่มขึ้นของคุณและทุกตัวอักษรไม่มีรอบอีกต่อไปเพื่อ1e999 +Infinityตามกฎหมายของ Murphy นี่เป็นการทำลายอัลกอริทึม แย่กว่านั้น: โปรแกรมเมอร์ที่เป็นมนุษย์ที่สร้าง "128 บิต" จะไม่พบข้อผิดพลาดนั้นล่วงหน้า กล่าวคือมักจะสายเกินไปเมื่อพบและรับรู้ข้อผิดพลาดนี้ อันตรายมาก.
ulidtko

1
แน่นอนว่าสถานการณ์เลวร้ายที่สุดข้างต้นอาจยังห่างไกลจากความเป็นจริง แต่ยังคงพิจารณาทางเลือกอื่น! อยู่ในด้านที่ปลอดภัยจะดีกว่า
ulidtko

2
"ในอีก 20 ปี" หึ มาเลย. คำตอบนี้ไม่ว่าไม่ดี
alecov

@ulidtko ฉันไม่ชอบสิ่งนี้ แต่จริงๆเหรอ?
Iharob Al Asimi

0

นี่เป็นวิธีง่ายๆในการกำหนดค่าคงที่และฉันค่อนข้างมั่นใจว่ามันพกพาได้:

เมื่อฉันเรียกใช้รหัสนี้:

ฉันเข้าใจ:

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.