มีฟังก์ชั่นสัญญาณมาตรฐาน (signum, sgn) ใน C / C ++ หรือไม่


409

ฉันต้องการฟังก์ชั่นที่คืนค่า -1 สำหรับจำนวนลบและ +1 สำหรับจำนวนบวก http://en.wikipedia.org/wiki/Sign_function มันง่ายพอที่จะเขียนของฉันเอง แต่ดูเหมือนว่าบางสิ่งที่ควรจะอยู่ในห้องสมุดมาตรฐานที่ไหนสักแห่ง

แก้ไข: โดยเฉพาะฉันกำลังมองหาฟังก์ชั่นที่ทำงานกับลอย


13
สิ่งที่ควรกลับเป็น 0
Craig McQueen

61
@ Craig McQueen; ขึ้นอยู่กับว่ามันเป็นศูนย์บวกหรือลบศูนย์
ysth

1
ฉันสังเกตเห็นว่าคุณระบุค่าที่ส่งคืนเป็นจำนวนเต็ม คุณกำลังมองหาวิธีแก้ปัญหาที่ใช้จำนวนเต็มหรือตัวเลขทศนิยม?
Mark Byers

6
@ysth @Craig McQueen เป็นเท็จสำหรับการลอยด้วยใช่ไหม SGN (x) 's นิยามบอกว่าจะกลับ 0 x==0ถ้า จากข้อมูลของIEEE 754ค่าศูนย์ลบและศูนย์บวกควรเปรียบเทียบเท่ากัน
RJFalconer

5
@ysth "มันขึ้นอยู่กับศูนย์บวกหรือศูนย์ลบ" ในความเป็นจริงมันไม่ได้
RJFalconer

คำตอบ:


506

แปลกใจที่ไม่มีใครโพสต์รุ่น C ++ ที่ปลอดภัยประเภท:

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

ประโยชน์ที่ได้รับ:

  • ใช้สัญลักษณ์จริง (-1, 0 หรือ 1) การนำไปใช้ที่นี่โดยใช้ copysign จะส่งคืนค่า -1 หรือ 1 เท่านั้นซึ่งไม่ใช่ signum นอกจากนี้การใช้งานบางอย่างที่นี่กลับมาลอย (หรือ T) มากกว่า int ซึ่งดูเหมือนว่าสิ้นเปลือง
  • ใช้งานได้กับ ints ลอยคู่กางเกงขาสั้นที่ไม่ได้ลงชื่อหรือประเภทที่กำหนดเองใด ๆ ที่สร้างได้จากจำนวนเต็ม 0 และสามารถสั่งซื้อได้
  • ด่วน! copysignช้าโดยเฉพาะถ้าคุณต้องการโปรโมตและ จำกัด ให้แคบลงอีกครั้ง นี่คือไม่มีสาขาและเพิ่มประสิทธิภาพอย่างยอดเยี่ยม
  • มาตรฐาน! แฮ็ก bitshift นั้นเรียบร้อย แต่ใช้ได้กับการเป็นตัวแทนเพียงเล็กน้อยและจะไม่ทำงานเมื่อคุณมีประเภทที่ไม่ได้ลงนาม มันสามารถให้เป็นความเชี่ยวชาญคู่มือเมื่อเหมาะสม
  • ถูกต้อง! การเปรียบเทียบแบบง่าย ๆ กับศูนย์สามารถรักษาการแสดงภายในที่มีความแม่นยำสูงของเครื่อง (เช่น 80 บิตบน x87) และหลีกเลี่ยงรอบก่อนกำหนดเป็นศูนย์

คำเตือน:

  • มันเป็นเทมเพลตดังนั้นจึงอาจใช้เวลานานในการรวบรวมในบางสถานการณ์
  • เห็นได้ชัดว่าบางคนคิดว่าการใช้ฟังก์ชั่นไลบรารี่มาตรฐานใหม่ที่ค่อนข้างลึกลับและช้ามากที่ไม่ได้ใช้ Signum จริงๆเป็นที่เข้าใจได้มากกว่า
  • < 0ส่วนหนึ่งของการตรวจสอบเป็นต้นเหตุของ GCC -Wtype-limitsเตือนเมื่อ instantiated สำหรับประเภทที่ไม่ได้ลงชื่อ คุณสามารถหลีกเลี่ยงปัญหานี้ได้ด้วยการใช้งานเกินพิกัดบางอย่าง:

    template <typename T> inline constexpr
    int signum(T x, std::false_type is_signed) {
        return T(0) < x;
    }
    
    template <typename T> inline constexpr
    int signum(T x, std::true_type is_signed) {
        return (T(0) < x) - (x < T(0));
    }
    
    template <typename T> inline constexpr
    int signum(T x) {
        return signum(x, std::is_signed<T>());
    }

    (ซึ่งเป็นตัวอย่างที่ดีของข้อแม้แรก)


18
@GMan: GCC เพียงแค่ตอนนี้ (4.5) หยุดมีกำลังสองเท่ากับราคาของการสร้างอินสแตนซ์สำหรับฟังก์ชันเทมเพลตและยังคงมีราคาแพงในการแยกวิเคราะห์และสร้างอินสแตนซ์มากกว่าฟังก์ชั่นที่เขียนด้วยตนเองหรือ C preprocessor มาตรฐาน ตัวเชื่อมโยงต้องทำงานเพิ่มเติมเพื่อลบอินสแตนซ์ที่ซ้ำกัน เทมเพลตยังสนับสนุน # include-in- # include ซึ่งทำให้การคำนวณการพึ่งพาต้องใช้เวลานานและการเปลี่ยนแปลงเล็ก ๆ (มักจะนำไปใช้งานไม่ใช่ส่วนต่อประสาน) เพื่อบังคับให้มีการคอมไพล์ไฟล์มากขึ้น

15
@ โจ: ใช่และยังไม่มีค่าใช้จ่ายที่เห็นได้ชัด C ++ ใช้เทมเพลตซึ่งเป็นเพียงสิ่งที่เราทุกคนต้องเข้าใจยอมรับและเอาชนะ
GManNickG

42
เดี๋ยวก่อนธุรกิจ "copysign ช้า" คืออะไร ... การใช้คอมไพเลอร์ปัจจุบัน (g ++ 4.6+, เสียงดังกราว ++ 3.0), std::copysignดูเหมือนว่าจะทำให้โค้ดที่ยอดเยี่ยมสำหรับฉัน: 4 คำแนะนำ (แบบอินไลน์), ไม่มีการแตกแขนง, โดยใช้ FPU ทั้งหมด สูตรที่กำหนดในคำตอบนี้โดยคมชัดสร้างรหัสที่เลวร้ายมาก (คำแนะนำอื่น ๆ อีกมากมายรวมทั้งคูณย้ายไปมาระหว่างหน่วยจำนวนเต็มและ FPU) ...
snogglethorpe

14
@snogglethorpe: หากคุณกำลังเรียกcopysignหา int มันจะช่วยให้คุณลอย / สองครั้งและจะต้องแคบลงอีกครั้งเมื่อกลับมา คอมไพเลอร์ของคุณอาจปรับการโปรโมตนั้นให้ดีที่สุด แต่ฉันไม่พบสิ่งที่แนะนำที่รับประกันโดยมาตรฐาน นอกจากนี้ในการติดตั้ง Signum ผ่าน copysign คุณจำเป็นต้องจัดการด้วยตัวเอง 0 กรณี - โปรดตรวจสอบให้แน่ใจว่าคุณได้รวมไว้ในการเปรียบเทียบประสิทธิภาพใด ๆ

53
รุ่นแรกไม่แตกกิ่งก้านสาขา ทำไมคนคิดว่าการเปรียบเทียบที่ใช้ในการแสดงออกจะไม่สร้างสาขา? มันจะเกี่ยวกับสถาปัตยกรรมส่วนใหญ่ เฉพาะโปรเซสเซอร์ที่มี cmove (หรือการคาดการณ์ล่วงหน้า) เท่านั้นที่จะสร้างรหัส branchless แต่พวกเขาจะทำเช่นนั้นสำหรับ ternaries หรือถ้า / อื่น ๆ ถ้ามันเป็นผู้ชนะ
Patrick Schlüter

271

ฉันไม่รู้ฟังก์ชันมาตรฐานสำหรับมัน นี่เป็นวิธีที่น่าสนใจในการเขียน:

(x > 0) - (x < 0)

นี่เป็นวิธีที่อ่านได้มากขึ้น:

if (x > 0) return 1;
if (x < 0) return -1;
return 0;

ถ้าคุณชอบผู้ประกอบการที่คุณสามารถทำสิ่งนี้:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)

7
x==0มาร์คไถ่การแสดงออกของคุณให้ผลลัพธ์ที่ไม่ถูกต้องสำหรับ
avakar

3
@Svante: "แต่ละของผู้ประกอบการ<, >... จะบังเกิด 1 ถ้าความสัมพันธ์ที่ระบุเป็นความจริงและ 0 ถ้ามันเป็นเท็จ"
สตีเฟ่น Canon

11
@Svante: ไม่แน่นอน ค่าของ0คือ "false"; ค่าอื่น ๆ คือ "true"; อย่างไรก็ตามผู้ประกอบการสัมพันธ์และความเสมอภาคจะกลับมา0หรือ1(ดูมาตรฐาน 6.5.8 และ 6.5.9) - มูลค่าของการแสดงออกa * (x == 42)อย่างใดอย่างหนึ่งหรือ0 a
pmg

21
มาร์คประสิทธิภาพสูงฉันประหลาดใจที่คุณพลาดแท็ก C ++ คำตอบนี้ถูกต้องมากและไม่สมควรลงคะแนน ยิ่งกว่านั้นฉันจะไม่ใช้copysignอินทิกรัลxถึงแม้ว่าฉันจะมีให้ก็ตาม
avakar

6
มีใครตรวจสอบรหัส GCC / G ++ / คอมไพเลอร์อื่น ๆ ที่ปล่อยออกมาบนแพลตฟอร์มจริงหรือไม่? ฉันเดาว่ารุ่น "ไร้สาขา" ใช้สองสาขาแทนหนึ่งสาขา Bitshifting น่าจะเร็วกว่ามาก - และพกพาได้มากกว่าในแง่ของประสิทธิภาพ
Jørgen Fogh

192

มีฟังก์ชั่นห้องสมุดคณิตศาสตร์ C99 ที่เรียกว่า copysign () ซึ่งรับสัญญาณจากการโต้แย้งหนึ่งและค่าสัมบูรณ์จากที่อื่น:

result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double

จะให้ผลลัพธ์เป็น +/- 1.0 ขึ้นอยู่กับสัญลักษณ์ของมูลค่า โปรดทราบว่าเลขศูนย์ทศนิยมถูกเซ็นชื่อ: (+0) จะให้ผลเป็น +1 และ (-0) จะให้ผลเป็น -1


57
โหวตขึ้นโหวตหนึ่งคำตอบนี้ได้รับความนิยมมากที่สุด หลงทางอย่างน่าประหลาดใจที่ชุมชน SO ดูเหมือนจะชอบแฮ็คเพื่อใช้ฟังก์ชั่นห้องสมุดมาตรฐาน ขอให้เทพแห่งการเขียนโปรแกรมประณามพวกคุณทุกคนพยายามที่จะถอดรหัสแฮ็คที่โปรแกรมเมอร์ฉลาดใช้ที่ไม่คุ้นเคยกับมาตรฐานภาษา ใช่ฉันรู้ว่านี่จะเสียค่าใช้จ่ายตัวแทนมากมายใน SO แต่ฉันค่อนข้างจะมาพร้อมกับการมาถึงกว่าผู้อื่นที่เหลือของคุณ ...
Mark ประสิทธิภาพสูง

34
สิ่งนี้อยู่ใกล้ แต่ให้คำตอบที่ผิดสำหรับศูนย์ (อ้างอิงจากบทความ Wikipedia ในคำถามอย่างน้อย) ข้อเสนอแนะที่ดีแม้ว่า +1 อยู่ดี
Mark Byers

4
หากคุณต้องการจำนวนเต็มหรือถ้าคุณต้องการผลลัพธ์ Signum ที่แน่นอนสำหรับเลขศูนย์ฉันชอบคำตอบของ Mark Byers ซึ่งสง่างามมาก! หากคุณไม่สนใจเกี่ยวกับข้างต้น copysign () อาจมีความได้เปรียบด้านประสิทธิภาพขึ้นอยู่กับแอปพลิเคชัน - ถ้าฉันปรับลูปที่สำคัญให้เหมาะสมฉันจะลองทั้งสองอย่าง
comingstorm

10
1) C99 ไม่รองรับทุกที่ (พิจารณา VC ++) 2) นี่เป็นคำถาม C ++ นี่เป็นคำตอบที่ดี แต่คำตอบที่ดีก็ใช้ได้เช่นกันและใช้ได้อย่างกว้างขวางยิ่งขึ้น
Pavel Minaev

5
ผู้ช่วยให้รอด! จำเป็นต้องมีวิธีการตรวจสอบระหว่าง -0.0 และ 0.0
Ólafur Waage

79

ดูเหมือนว่าคำตอบส่วนใหญ่ไม่ได้ตอบคำถามดั้งเดิม

มีฟังก์ชั่นสัญญาณมาตรฐาน (signum, sgn) ใน C / C ++ หรือไม่

ไม่ได้อยู่ในไลบรารีมาตรฐาน แต่มีวิธีการcopysignที่สามารถใช้ได้เกือบเหมือนกันผ่านทางcopysign(1.0, arg)และมีฟังก์ชั่นการลงชื่อเข้าใช้จริงboostซึ่งอาจเป็นส่วนหนึ่งของมาตรฐานด้วย

    #include <boost/math/special_functions/sign.hpp>

    //Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero.
    template <class T>
    inline int sign (const T& z);

http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html


5
นี่ควรเป็นคำตอบที่ได้รับการโหวตมากที่สุดเนื่องจากเป็นคำตอบที่ใกล้เคียงที่สุดสำหรับสิ่งที่ถามในคำถาม
BartoszKP

ฉันสงสัยในช่วงสองสามนาทีที่ผ่านมาว่าทำไมไลบรารีมาตรฐานจึงไม่มีฟังก์ชั่นการลงชื่อ มันเป็นเรื่องธรรมดามาก - ใช้กันอย่างแพร่หลายมากกว่าฟังก์ชั่นแกมม่าซึ่งสามารถพบได้ในส่วนหัวของ cmath
Taozi

4
คำอธิบายที่ฉันมักจะได้รับสำหรับคำถามที่คล้ายกันคือ "มันง่ายพอที่จะทำให้ตัวเอง" ซึ่ง IMO ไม่ใช่เหตุผลที่ดี มันเป็นปัญหาอย่างสมบูรณ์ของการที่มาตรฐาน, กรณีขอบไม่ชัดเจนและสถานที่ที่จะนำเครื่องมือที่ใช้กันอย่างแพร่หลาย
Catskul

77

เห็นได้ชัดว่าคำตอบของคำถามโปสเตอร์ต้นฉบับคือไม่มี ไม่มีฟังก์ชั่นC ++ มาตรฐานsgn


2
@SR_ คุณไม่ถูกต้อง copysign()จะไม่ทำให้พารามิเตอร์แรกของคุณ 0.0 หากตัวที่สองคือ 0.0 กล่าวอีกนัยหนึ่งว่าจอห์นถูกต้อง
Alexis Wilke

30

มีฟังก์ชั่นสัญญาณมาตรฐาน (signum, sgn) ใน C / C ++ หรือไม่

ใช่ขึ้นอยู่กับคำจำกัดความ

C99 และใหม่กว่ามีsignbit()มาโคร<math.h>

int signbit(ลอยจริงx); แมโครผลตอบแทนค่าไม่ใช่ศูนย์ถ้าหากสัญญาณของค่าอาร์กิวเมนต์เป็นลบ C11 §7.12.3.6
signbit


ทว่า OP ต้องการบางสิ่งที่แตกต่างออกไปเล็กน้อย

ฉันต้องการฟังก์ชั่นที่คืนค่า -1 สำหรับจำนวนลบและ +1 สำหรับจำนวนบวก ... ฟังก์ชั่นที่ทำงานกับโฟลต

#define signbit_p1_or_n1(x)  ((signbit(x) ?  -1 : 1)

ลึก:

x = 0.0, -0.0, +NaN, -NaNโพสต์ไม่ได้เฉพาะในกรณีต่อไปนี้:

คลาสสิกsignum()ผลตอบแทน+1บนx>0, -1บนx<0และบน0x==0

มีคำตอบมากมายที่ได้กล่าวไปแล้ว แต่ไม่ได้x = -0.0, +NaN, -NaNกล่าวถึง หลายคนกำลังมุ่งสำหรับจำนวนเต็มจุดของมุมมองที่มักจะขาดไม่-a-เบอร์ (เป็นน่าน ) และ-0.0

คำตอบทั่วไปทำงานเหมือนsignnum_typical() เมื่อวันที่พวกเขากลับ-0.0, +NaN, -NaN0.0, 0.0, 0.0

int signnum_typical(double x) {
  if (x > 0.0) return 1;
  if (x < 0.0) return -1;
  return 0;
}

ฉันขอเสนอฟังก์ชั่นนี้: วันที่-0.0, +NaN, -NaNมันกลับ-0.0, +NaN, -NaNมา

double signnum_c(double x) {
  if (x > 0.0) return 1.0;
  if (x < 0.0) return -1.0;
  return x;
}

1
อาสิ่งที่ฉันตามมา สิ่งนี้เพิ่งเปลี่ยนไปใน Pharo Smalltalk github.com/pharo-project/pharo/pull/1835และฉันสงสัยว่ามีมาตรฐาน (IEC 60559 หรือ ISO 10967) บางอย่างสำหรับพฤติกรรมลบศูนย์และพฤติกรรมน่าน ... ฉันชอบ javascript sign developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

29

เร็วกว่าโซลูชันด้านบนซึ่งรวมถึงอันดับสูงสุด:

(x < 0) ? -1 : (x > 0)

1
x คืออะไร หรือคุณใช้ #define อยู่
โอกาส

3
ประเภทของคุณไม่เร็วขึ้น มันจะทำให้แคชพลาดบ่อย
Jeffrey Drake

19
แคชพลาดไหม ฉันไม่แน่ใจ บางทีคุณอาจหมายถึงการคาดเดาผิดทางสาขา?
Catskul

2
สำหรับฉันแล้วมันจะส่งผลให้เกิดการเตือนถึงประเภทจำนวนเต็มและบูลีนที่สับสน!
sergiol

จะเร็วแค่ไหนกับกิ่งไม้?
Nick

16

มีวิธีการทำโดยไม่แตกกิ่งก้านสาขา แต่ก็ไม่สวยมาก

sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));

http://graphics.stanford.edu/~seander/bithacks.html

อีกมากมายที่น่าสนใจและฉลาดเกินกว่าในหน้านั้นอีกด้วย ...


1
ถ้าผมอ่านการเชื่อมโยงได้อย่างถูกต้องว่าผลตอบแทนเพียง -1 หรือ 0 ถ้าคุณต้องการ -1, 0 หรือ 1 แล้วมันหรือsign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)); sign = (v > 0) - (v < 0);
Z boson

1
นี่หมายความว่าvเป็นประเภทจำนวนเต็มไม่กว้างกว่า int
phuclv

12

หากสิ่งที่คุณต้องการคือการทดสอบสัญญาณให้ใช้signbit (ส่งกลับจริงถ้าอาร์กิวเมนต์มีเครื่องหมายลบ) ไม่แน่ใจว่าทำไมคุณถึงต้องการส่งคืนค่าเป็น -1 หรือ +1; copysign นั้นสะดวกกว่า แต่ดูเหมือนว่าจะส่งคืน +1 สำหรับศูนย์ลบในบางแพลตฟอร์มที่สนับสนุนเฉพาะบางส่วนสำหรับศูนย์ลบ


7
มีแอปพลิเคชันทางคณิตศาสตร์มากมายที่จำเป็นต้องมีเครื่องหมาย (x) if (x < 0)มิฉะนั้นผมแค่ต้องการทำ
โอกาส

5

โดยทั่วไปไม่มีฟังก์ชั่นเซ็นมาตรฐานใน C / C ++ และการขาดฟังก์ชั่นพื้นฐานดังกล่าวจะบอกคุณมากมายเกี่ยวกับภาษาเหล่านี้

นอกจากนั้นฉันเชื่อว่ามุมมองส่วนใหญ่ทั้งสองเกี่ยวกับวิธีการที่ถูกต้องในการกำหนดฟังก์ชั่นดังกล่าวนั้นถูกต้องและ "การโต้เถียง" เกี่ยวกับมันเป็นความจริงที่ไม่ใช่ข้อโต้แย้งเมื่อคุณคำนึงถึงสองประการสำคัญ:

  • Signumฟังก์ชั่นควรกลับชนิดของตัวถูกดำเนินการของตนในทำนองเดียวกันไปยังabs()ฟังก์ชั่นเพราะSignumมักจะใช้สำหรับการคูณกับค่าแน่นอนหลังจากที่หลังได้รับการประมวลผลอย่างใด ดังนั้นกรณีการใช้งานที่สำคัญของsignumไม่ใช่การเปรียบเทียบ แต่เป็นเลขคณิตและกรณีหลังนั้นไม่ควรเกี่ยวข้องกับการแปลงจำนวนเต็มเป็นถึง / จากจุดลอยตัวที่มีราคาแพง

  • ชนิดจุดลอยตัวไม่มีคุณลักษณะเป็นค่าศูนย์ที่แน่นอนเดียว: +0.0 สามารถตีความได้ว่า "อนันต์เหนือศูนย์" และ -0.0 เป็น "อนันต์ต่ำกว่าศูนย์" นั่นคือเหตุผลที่การเปรียบเทียบที่เกี่ยวข้องกับศูนย์ต้องตรวจสอบค่าทั้งสองและการแสดงออกเช่นx == 0.0นั้นอาจเป็นอันตรายได้

เกี่ยวกับ C ฉันคิดว่าวิธีที่ดีที่สุดที่มีประเภทอินทิกรัลคือการใช้(x > 0) - (x < 0)นิพจน์เนื่องจากมันควรจะแปลในแบบที่ไม่มีสาขาและต้องการการดำเนินงานพื้นฐานเพียงสามครั้งเท่านั้น ที่ดีที่สุดกำหนดฟังก์ชั่นแบบอินไลน์ที่บังคับใช้ประเภทผลตอบแทนที่ตรงกับประเภทอาร์กิวเมนต์และเพิ่ม C11 define _Genericเพื่อแมปฟังก์ชั่นเหล่านี้กับชื่อสามัญ

ที่มีค่าจุดลอยผมคิดว่าฟังก์ชั่นแบบอินไลน์บนพื้นฐาน C11 copysignf(1.0f, x), copysign(1.0, x)และcopysignl(1.0l, x)เป็นวิธีที่จะไปเพียงเพราะพวกเขายังมีแนวโน้มที่จะเป็นสาขาฟรีและยังไม่จำเป็นต้องหล่อเป็นผลมาจากจำนวนเต็มกลับเข้ามาในจุดลอย ราคา. คุณควรแสดงความคิดเห็นอย่างเด่นชัดว่าการใช้งานจุดลอยตัวของsignumของคุณจะไม่กลับมาเป็นศูนย์เพราะลักษณะของค่าศูนย์จุดลอยตัวการพิจารณาเวลาในการประมวลผลและเพราะมันมักจะมีประโยชน์มากในเลขคณิตจุดลอยตัวเพื่อรับ -1 / + 1 เครื่องหมายแม้เป็นค่าศูนย์


5

สำเนา C ของฉันในสั้นเผยให้เห็นการมีอยู่ของฟังก์ชั่นมาตรฐานที่เรียกว่า copysign ซึ่งอาจมีประโยชน์ ดูเหมือนว่า copysign (1.0, -2.0) จะคืนค่า -1.0 และ copysign (1.0, 2.0) จะคืนค่า +1.0

ใกล้ชิดเหรอ?


ไม่ได้มาตรฐาน แต่อาจมีอยู่ทั่วไป Microsoft เริ่มต้นด้วยขีดล่างซึ่งเป็นแบบแผนที่ใช้สำหรับส่วนขยายที่ไม่ได้มาตรฐาน ไม่ใช่ตัวเลือกที่ดีที่สุดเมื่อคุณทำงานกับจำนวนเต็ม
Mark Ransom

5
copysign มีทั้งใน ISO C (C99) และมาตรฐาน POSIX ดูopengroup.org/onlinepubs/000095399/functions/copysign.html
lhf

3
สิ่งที่ lhf พูด Visual Studio ไม่ใช่การอ้างอิงสำหรับมาตรฐาน C
สตีเฟ่น Canon

3

ไม่มันไม่มีอยู่ใน c ++ เหมือนใน matlab ฉันใช้มาโครในโปรแกรมของฉันสำหรับสิ่งนี้

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )

5
ควรใช้เทมเพลตมากกว่ามาโครใน C ++
Ruslan

ใน C ไม่มีแม่แบบ ...... helloacm.com/how-to-implement-the-sgn-function-in-c
doctorlai

ฉันคิดว่านี่เป็นคำตอบที่ดีจากนั้นฉันก็ดูรหัสของตัวเองและพบสิ่งนี้: #define sign(x) (((x) > 0) - ((x) < 0))ซึ่งก็ดีเช่นกัน
Michel Rouzic

1
ฟังก์ชั่นแบบอินไลน์จะดีกว่าแมโครใน C และ C ++ แม่แบบที่ดีกว่า
phuclv

3

ตอบรับกับการโอเวอร์โหลดด้านล่างไม่แน่นอนไม่เรียก-Wtype วงเงิน

template <typename T> inline constexpr
  int signum(T x, std::false_type) {
  return T(0) < x;
}

template <typename T> inline constexpr
  int signum(T x, std::true_type) {
  return (T(0) < x) - (x < T(0));
}

template <typename T> inline constexpr
  int signum(T x) {
  return signum(x, std::is_signed<T>());
}

สำหรับ C ++ 11 ทางเลือกอาจเป็นได้

template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T const x) {
    return T(0) < x;  
}

template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T const x) {
    return (T(0) < x) - (x < T(0));  
}

สำหรับฉันมันไม่เรียกใช้คำเตือนใด ๆ ใน GCC 5.3.1


เพื่อหลีกเลี่ยง-Wunused-parameterคำเตือนเพียงใช้พารามิเตอร์ที่ไม่มีชื่อ
โจนาธาน Wakely

นั่นเป็นเรื่องจริงมาก ฉันคิดถึงสิ่งนั้น อย่างไรก็ตามฉันชอบตัวเลือก C ++ 11 มากกว่านี้
SamVanDonut

2

บิตนอกหัวข้อ แต่ฉันใช้สิ่งนี้:

template<typename T>
constexpr int sgn(const T &a, const T &b) noexcept{
    return (a > b) - (a < b);
}

template<typename T>
constexpr int sgn(const T &a) noexcept{
    return sgn(a, T(0));
}

และฉันพบฟังก์ชั่นแรก - อันที่มีสองข้อโต้แย้ง, จะมีประโยชน์มากขึ้นจาก "มาตรฐาน" sgn () เพราะมันมักจะใช้ในรหัสเช่นนี้:

int comp(unsigned a, unsigned b){
   return sgn( int(a) - int(b) );
}

เมื่อเทียบกับ

int comp(unsigned a, unsigned b){
   return sgn(a, b);
}

ไม่มีเพี้ยนสำหรับประเภทที่ไม่ได้ลงชื่อและไม่มีการลบเพิ่มเติม

อันที่จริงฉันมีรหัสชิ้นนี้ใช้ sgn ()

template <class T>
int comp(const T &a, const T &b){
    log__("all");
    if (a < b)
        return -1;

    if (a > b)
        return +1;

    return 0;
}

inline int comp(int const a, int const b){
    log__("int");
    return a - b;
}

inline int comp(long int const a, long int const b){
    log__("long");
    return sgn(a, b);
}

1

คำถามเก่า แต่ตอนนี้มีฟังก์ชั่นที่ต้องการเช่นนี้ ฉันเพิ่ม wrapper โดยไม่เหลือกะและธันวาคม

คุณสามารถใช้ฟังก์ชั่น wrapper ตามsignbit จาก C99เพื่อให้ได้พฤติกรรมตามที่ต้องการ (ดูรหัสเพิ่มเติมด้านล่าง)

ส่งคืนว่าเครื่องหมายของ x เป็นลบหรือไม่
นอกจากนี้ยังสามารถนำไปใช้กับ infinites, NaNs และ zeroes (ถ้าศูนย์ไม่ได้ลงนามก็ถือว่าเป็นบวก

#include <math.h>

int signValue(float a) {
    return ((!signbit(a)) << 1) - 1;
}

หมายเหตุ: ฉันใช้ตัวถูกดำเนินการไม่ ("!") เพราะค่าตอบแทนของ signbit ไม่ได้ระบุว่าเป็น 1 (แม้ว่าตัวอย่างให้เราคิดว่ามันจะเป็นเช่นนี้เสมอ) แต่จริงสำหรับจำนวนลบ:

Return value ค่าที่
ไม่ใช่ศูนย์ (จริง) หากเครื่องหมายของ x เป็นลบ; และศูนย์ (เท็จ) มิฉะนั้น

จากนั้นฉันคูณสองด้วยการเลื่อนซ้าย ("<< 1") ซึ่งจะให้เรา 2 สำหรับจำนวนบวกและ 0 สำหรับหนึ่งลบหนึ่งและลดลงในที่สุด 1 เพื่อให้ได้ 1 และ -1 สำหรับตัวเลขบวกและลบตามลำดับตามที่ร้องขอโดย OP


0 จะเป็นบวกเกินไปแล้ว ... ซึ่งอาจจะหรืออาจจะไม่ได้สิ่งที่ต้องการ ... OP
Antti Haapala

ทีนี้เราอาจไม่มีทางรู้ว่า OP ต้องการอย่างแท้จริงถ้า n = 0 ... !
Antonin GAVREL

0

ในขณะที่วิธีแก้ปัญหาจำนวนเต็มในคำตอบที่ยอมรับนั้นค่อนข้างหรูหรา แต่ก็รบกวนฉันว่ามันจะไม่สามารถคืน NAN สำหรับประเภทสองประเภทได้ดังนั้นฉันจึงปรับเปลี่ยนเล็กน้อย

template <typename T> double sgn(T val) {
    return double((T(0) < val) - (val < T(0)))/(val == val);
}

โปรดทราบว่าการส่งคืนจุดลอย NAN ซึ่งตรงข้ามกับฮาร์ดโค้ดNANทำให้เกิดการตั้งค่าบิตในการใช้งานบางอย่างดังนั้นเอาต์พุตสำหรับval = -NANและval = NANจะเหมือนกันไม่ว่าจะเกิดอะไรขึ้น (ถ้าคุณชอบnanเอาต์พุต "" มากกว่าที่-nanคุณสามารถใส่ได้ " abs(val)ก่อนที่จะส่งกลับ ... )


0

คุณสามารถใช้boost::math::sign()วิธีการboost/math/special_functions/sign.hppถ้ามีการเพิ่ม


หมายเหตุที่ว่านี้ได้รับการแนะนำมาก่อน: stackoverflow.com/a/16869019/1187415
Martin R

0

นี่คือการติดตั้งที่ใช้งานง่าย

inline int signum(const double x) {
    if(x == 0) return 0;
    return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

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

อีกทางหนึ่งในคอมไพเลอร์และซีพียูบางรุ่นอาจไม่มีสาขาที่สมบูรณ์เร็วกว่า:

inline int signum(const double x) {
    return (x != 0) * 
        (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

งานนี้สำหรับIEEE 754 แม่นยำสองรูปแบบไบนารีจุดลอยตัว: binary64


-1
int sign(float n)
{     
  union { float f; std::uint32_t i; } u { n };
  return 1 - ((u.i >> 31) << 1);
}

ฟังก์ชั่นนี้ถือว่า:

  • การแสดงเลขฐานสองของเลขฐาน 32
  • คอมไพเลอร์ที่สร้างข้อยกเว้นเกี่ยวกับกฎนามแฝงที่เข้มงวดเมื่อใช้ยูเนี่ยนที่ตั้งชื่อแล้ว

3
ยังมีสมมติฐานที่ไม่ดีอยู่ที่นี่ ตัวอย่างเช่นฉันไม่เชื่อว่า endianness ของการลอยนั้นรับประกันว่าจะเป็น endianness ของจำนวนเต็ม เช็คของคุณล้มเหลวในสถาปัตยกรรมใด ๆ ที่ใช้ ILP64 จริง ๆ แล้วคุณเพียงแค่นำมาใช้copysignใหม่ หากคุณกำลังใช้static_assertคุณได้มี C ++ 11 copysignและอาจรวมทั้งการใช้งานจริงๆ


-3

เหตุใดจึงต้องใช้ตัวดำเนินการที่ประกอบไปด้วยและหากอื่นเมื่อคุณสามารถทำได้

#define sgn(x) x==0 ? 0 : x/abs(x)

3
คำจำกัดความของคุณใช้ผู้ประกอบการที่ประกอบไปด้วย
Martin R

ใช่แน่นอน แต่มันใช้โอเปอเรเตอร์หนึ่งตัวเพื่อแยกตัวเลขศูนย์และตัวเลขที่ไม่ใช่ศูนย์ รุ่นอื่น ๆ ได้แก่ opernary ประกอบไปด้วยเพื่อแยกบวกลบและศูนย์
Jagreet

การใช้การหารจำนวนเต็มนั้นไม่มีประสิทธิภาพมากและ abs () ใช้สำหรับจำนวนเต็มเท่านั้น
Michel Rouzic

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