C คำนวณบาป () และฟังก์ชันคณิตศาสตร์อื่น ๆ ได้อย่างไร


248

ฉันได้อ่านผ่านการแยกชิ้นส่วน. NET และซอร์สโค้ด GCC แต่ดูเหมือนไม่สามารถหาได้ทุกที่ที่การใช้งานจริงsin()และฟังก์ชั่นคณิตศาสตร์อื่น ๆ ... พวกเขาดูเหมือนจะอ้างถึงอย่างอื่นเสมอ

ใครสามารถช่วยฉันหาพวกเขาได้บ้าง ฉันรู้สึกว่ามันไม่น่าเป็นไปได้ที่ฮาร์ดแวร์ทั้งหมดที่ C จะทำงานบนรองรับฟังก์ชั่นตรีโกณฯ ในฮาร์ดแวร์ดังนั้นจะต้องมีอัลกอริทึมซอฟต์แวร์ที่ไหนสักแห่งใช่ไหม?


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


2
โปรดทราบว่าการติดตั้งนี้ขึ้นอยู่กับ คุณควรระบุว่าการดำเนินการใดที่คุณสนใจมากที่สุด
jason

3
ฉันติดแท็ก. NET และ C เพราะฉันดูทั้งสองที่และไม่สามารถเข้าใจได้ แม้ว่าการดูที่การถอดแยกชิ้นส่วน. NET ดูเหมือนว่ามันอาจจะถูกเรียกใช้เป็น C ที่ไม่มีการจัดการดังนั้นเท่าที่ฉันรู้ว่าพวกเขามีการใช้งานแบบเดียวกัน
แฮงค์

คำตอบ:


213

ใน GNU libm การใช้งานsinขึ้นอยู่กับระบบ ดังนั้นคุณสามารถหาการดำเนินการสำหรับแต่ละแพลตฟอร์มที่ไหนสักแห่งในไดเรกทอรีย่อยที่เหมาะสมของsysdeps

หนึ่งไดเร็กทอรีรวมถึงการนำไปใช้งานใน C ซึ่งสนับสนุนโดย IBM ตั้งแต่เดือนตุลาคม 2011 นี่คือรหัสที่ทำงานจริงเมื่อคุณโทรsin()บนระบบ x86-64 Linux ทั่วไป เห็นได้ชัดว่าเร็วกว่าfsinคำแนะนำในการประกอบ รหัสที่มา: sysdeps / IEEE754 / DBL-64 / s_sin.c__sin (double x)มองหา

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

  • เมื่อxเป็นอย่างมากใกล้เคียงกับ 0, sin(x) == xเป็นคำตอบที่ถูกต้อง

  • ไกลออกไปเล็กน้อยsin(x)ใช้ซีรี่ส์ของ Taylor ที่คุ้นเคย อย่างไรก็ตามนี่เป็นเพียงความแม่นยำใกล้ 0 ดังนั้น ...

  • เมื่อมุมมีค่ามากกว่า 7 °จะใช้อัลกอริธึมที่แตกต่างกันทำการคำนวณอนุกรมเทย์เลอร์สำหรับทั้ง sin (x) และ cos (x) จากนั้นใช้ค่าจากตารางที่คำนวณล่วงหน้าเพื่อปรับแต่งการประมาณ

  • เมื่อ | x | > 2 อัลกอริทึมข้างต้นจะไม่ทำงานดังนั้นรหัสเริ่มต้นด้วยการคำนวณค่าใกล้กับ 0 ที่สามารถป้อนsinหรือcosแทน

  • ยังมีอีกสาขาที่จัดการกับxที่เป็น NaN หรือไม่สิ้นสุด

รหัสนี้ใช้แฮ็คตัวเลขบางตัวที่ฉันไม่เคยเห็นมาก่อน แต่สำหรับทุกคนที่ฉันรู้ว่าพวกเขาอาจเป็นที่รู้จักในหมู่ผู้เชี่ยวชาญด้านจุดลอยตัว บางครั้งโค้ดไม่กี่บรรทัดอาจใช้เวลาหลายย่อหน้าในการอธิบาย ตัวอย่างเช่นสองบรรทัดนี้

double t = (x * hpinv + toint);
double xn = t - toint;

ถูกนำมาใช้ (บางครั้ง) ในการลดค่าxเป็นค่าที่ใกล้เคียงกับ 0 ซึ่งแตกต่างจากxโดยผลคูณของπ / 2 โดยเฉพาะxn×π / 2 วิธีการนี้ทำได้โดยไม่ต้องแบ่งแยกหรือแยกสาขาค่อนข้างฉลาด แต่ไม่มีความคิดเห็นเลย!


GCC / glibc รุ่น 32 บิตที่เก่ากว่าใช้fsinคำสั่งซึ่งไม่ถูกต้องอย่างน่าประหลาดใจสำหรับอินพุตบางตัว มีเป็นบล็อกโพสต์ที่น่าสนใจที่แสดงนี้มีเพียง 2 สายรหัส

การติดตั้ง fdlibm sinใน pure C นั้นง่ายกว่า glibc และให้ความเห็นเป็นอย่างดี รหัสที่มา: fdlibm / s_sin.cและfdlibm / k_sin.c


35
จะเห็นว่านี้เป็นจริงรหัสที่ทำงานบน x86: รวบรวมโปรแกรมที่โทรsin(); พิมพ์gdb a.outแล้วbreak sinจากนั้นแล้วrun disassemble
Jason Orendorff

5
@Henry: อย่าคิดผิดที่คิดว่าเป็นรหัสที่ดี มันแย่มากๆ อย่าเรียนรู้ที่จะใช้รหัส!
โทมัส Bonini

2
@Andreas อืมคุณพูดถูกรหัส IBM ก็ดูแย่มากเมื่อเทียบกับ fdlibm ฉันแก้ไขคำตอบเพื่อเพิ่มลิงก์ไปยังรูทีนไซน์ของ fdlibm
Jason Orendorff

3
@Henry: __kernel_sinถูกกำหนดเป็น k_sin.c และบริสุทธิ์ C คลิกอีกครั้งฉันใส่ URL เรียบร้อยในครั้งแรก
Jason Orendorff

3
รหัส sysdeps ที่เชื่อมโยงนั้นน่าสนใจเป็นพิเศษเพราะมันถูกปัดเศษอย่างถูกต้อง นั่นคือมันจะให้คำตอบที่ดีที่สุดเท่าที่จะเป็นไปได้สำหรับค่าอินพุตทั้งหมด ในบางกรณีสิ่งนี้อาจช้าเพราะต้องคำนวณตัวเลขพิเศษจำนวนมากเพื่อให้แน่ใจว่าการปัดเศษถูกต้อง ในกรณีอื่น ๆ มันเร็วมาก - สำหรับจำนวนที่น้อยพอคำตอบก็คือมุม
Bruce Dawson

67

ฟังก์ชันเช่นไซน์และโคไซน์นำมาใช้ในไมโครโค้ดภายในไมโครโปรเซสเซอร์ ตัวอย่างเช่นชิป Intel มีคำแนะนำการประกอบสำหรับสิ่งเหล่านี้ คอมไพเลอร์ AC จะสร้างรหัสที่เรียกคำแนะนำการประกอบเหล่านี้ (โดยทางตรงข้ามคอมไพเลอร์ Java จะไม่ Java ประเมินฟังก์ชันตรีโกณฯ ในซอฟต์แวร์มากกว่าฮาร์ดแวร์และดังนั้นจึงทำงานช้าลงมาก)

ชิปไม่ได้ใช้ซีรีย์ของ Taylor เพื่อคำนวณฟังก์ชั่นตรีโกณมิติอย่างน้อยก็ไม่ใช่ทั้งหมด ก่อนอื่นพวกเขาใช้CORDICแต่พวกเขาอาจใช้ซีรี่ส์สั้น ๆ ของ Taylor เพื่อขัดผลของ CORDIC หรือสำหรับกรณีพิเศษเช่นการคำนวณไซน์ที่มีความแม่นยำสัมพัทธ์สูงสำหรับมุมที่เล็กมาก สำหรับคำอธิบายเพิ่มเติมดูคำตอบ StackOverflowนี้


10
ฟังก์ชันทางคณิตศาสตร์ที่ยอดเยี่ยมเช่นไซน์และโคไซน์อาจนำไปใช้ในไมโครโค้ดหรือเป็นคำแนะนำฮาร์ดแวร์ในเดสก์ท็อป 32 บิตและเซิร์ฟเวอร์โปรเซสเซอร์ปัจจุบัน นี่ไม่ใช่กรณีเสมอไปจนกระทั่งการคำนวณจุดลอยตัวทั้งหมดของ i486 (DX) ในซอฟต์แวร์ ("soft-float") สำหรับซีรีย์ x86 โดยไม่มีตัวประมวลผลร่วมแยกกัน ไม่ใช่ทั้งหมดที่มี (FPUs) รวมถึงฟังก์ชันยอดเยี่ยม (เช่น Weitek 3167)
mctylr

1
คุณจะเจาะจงมากขึ้นได้ไหม? หนึ่ง "ขัดขึ้น" ประมาณโดยใช้ชุดเทย์เลอร์ได้อย่างไร
แฮงค์

4
เท่าที่คำตอบ "ขัดเกลา" สมมติว่าคุณกำลังคำนวณทั้งไซน์และโคไซน์ สมมติว่าคุณรู้ค่าที่แน่นอนของทั้งสองจุด (เช่นจาก CORDIC) แต่ต้องการค่าที่จุดใกล้เคียง จากนั้นสำหรับความแตกต่าง h คุณสามารถใช้การประมาณเทย์เลอร์ f (x + h) = f (x) + h f '(x) หรือ f (x + h) = f (x) + h f' (x) + h ^ 2 f '' (x) / 2
John D. Cook

6
ชิป x86 / x64 มีคำแนะนำการประกอบสำหรับการคำนวณไซน์ (fsin) แต่คำสั่งนี้บางครั้งค่อนข้างไม่ถูกต้องและดังนั้นจึงไม่ค่อยได้ใช้อีกต่อไป ดูrandomascii.wordpress.com/2014/10/09/…สำหรับรายละเอียด โปรเซสเซอร์อื่น ๆ ส่วนใหญ่ไม่มีคำแนะนำสำหรับไซน์และโคไซน์เนื่องจากการคำนวณในซอฟต์แวร์ให้ความยืดหยุ่นมากกว่าและอาจเร็วกว่า
Bruce Dawson

3
โดยทั่วไปแล้วสิ่งที่อยู่ภายในชิป Intel ไม่ได้ใช้ ก่อนอื่นความแม่นยำและความละเอียดของการดำเนินการเป็นสิ่งสำคัญอย่างยิ่งสำหรับแอปพลิเคชั่นมากมาย Cordic ไม่ถูกต้องฉาวโฉ่เมื่อคุณไปถึงหลักที่ 7 หรือมากกว่านั้นและคาดเดาไม่ได้ ประการที่สองฉันได้ยินว่ามีข้อบกพร่องในการใช้งานซึ่งทำให้เกิดปัญหามากขึ้น ฉันดูที่ฟังก์ชั่น sin สำหรับ linux gcc และแน่นอนว่ามันใช้ chebyshev ไม่มีการใช้สิ่งของในตัว โอ้, อัลกอริทึม Cordic ในชิปจะช้ากว่าโซลูชันซอฟต์แวร์
Donald Murray

63

ตกลง kiddies เวลาสำหรับข้อดี .... นี่คือหนึ่งในข้อร้องเรียนที่ใหญ่ที่สุดของฉันกับวิศวกรซอฟต์แวร์มือใหม่ พวกเขามาในการคำนวณฟังก์ชันยอดเยี่ยมตั้งแต่เริ่มต้น (ใช้ซีรี่ส์ของ Taylor) ราวกับว่าไม่มีใครเคยคำนวณมาก่อนในชีวิตของพวกเขา ไม่จริง. นี่เป็นปัญหาที่กำหนดไว้อย่างดีและได้รับการติดต่อนับพันครั้งโดยวิศวกรซอฟต์แวร์และฮาร์ดแวร์ที่ฉลาดมากและมีวิธีแก้ไขปัญหาที่ชัดเจน โดยทั่วไปฟังก์ชันยอดเยี่ยมส่วนใหญ่ใช้ชื่อพหุนาม Chebyshev เพื่อคำนวณ การใช้ชื่อพหุนามขึ้นอยู่กับสถานการณ์ ครั้งแรกพระคัมภีร์ในเรื่องนี้เป็นหนังสือที่เรียกว่า "การประมาณด้วยคอมพิวเตอร์" โดยฮาร์ตและเชนีย์ ในหนังสือเล่มนี้คุณสามารถตัดสินใจได้ว่าคุณมี adder ฮาร์ดแวร์ตัวคูณตัวหาร ฯลฯ และตัดสินใจว่าการดำเนินการใดที่เร็วที่สุด เช่นถ้าคุณมีตัวหารที่เร็วมาก วิธีที่เร็วที่สุดในการคำนวณไซน์อาจเป็น P1 (x) / P2 (x) โดยที่ P1, P2 คือชื่อพหุนาม Chebyshev หากไม่มีตัวแบ่งที่รวดเร็วอาจเป็นเพียง P (x) โดยที่ P มีคำศัพท์มากกว่า P1 หรือ P2 .... ดังนั้นมันจึงช้ากว่า ดังนั้นขั้นตอนแรกคือการกำหนดฮาร์ดแวร์ของคุณและสิ่งที่สามารถทำได้ จากนั้นคุณเลือกการผสมผสานที่เหมาะสมของชื่อพหุนาม Chebyshev (โดยปกติคือรูปแบบ cos (ax) = aP (x) สำหรับโคไซน์เช่นอีกครั้งโดยที่ P คือพหุนาม Chebyshev) จากนั้นคุณตัดสินใจว่าต้องการทศนิยมแม่นยำเท่าใด เช่นถ้าคุณต้องการความแม่นยำ 7 หลักคุณค้นหาในตารางที่เหมาะสมในหนังสือที่ฉันกล่าวถึงและมันจะให้ (สำหรับความแม่นยำ = 7.33) หมายเลข N = 4 และหมายเลขพหุนาม 3502 N คือคำสั่งของ พหุนาม (ดังนั้นจึงเป็น p4.x ^ 4 + p3.x ^ 3 + p2.x ^ 2 + p1.x + p0) เนื่องจาก N = 4 จากนั้นคุณค้นหาค่าจริงของ p4, p3, p2, p1, ค่า p0 ที่ด้านหลังของหนังสือเล่มต่ำกว่า 3502 (จะอยู่ในจุดลอย) จากนั้นคุณใช้อัลกอริทึมของคุณในซอฟต์แวร์ในรูปแบบ: ((((p4.x + p3) .x + p2) .x + p1) .x + p0 .... และนี่คือวิธีที่คุณคำนวณโคไซน์ถึงทศนิยม 7 วางบนฮาร์ดแวร์นั้น

โปรดทราบว่าการใช้งานฮาร์ดแวร์ส่วนใหญ่ของการดำเนินการยอดเยี่ยมใน FPU มักจะเกี่ยวข้องกับไมโครโค้ดและการดำเนินการเช่นนี้ (ขึ้นอยู่กับฮาร์ดแวร์) มีการใช้ชื่อพหุนาม Chebyshev สำหรับส่วนใหญ่ยอดเยี่ยม แต่ไม่ใช่ทั้งหมด เช่นรากที่สองนั้นเร็วกว่าที่จะใช้การวนซ้ำสองเท่าของวิธี Newton raphson โดยใช้ตารางการค้นหาก่อน อีกครั้งหนังสือ "การประมาณด้วยคอมพิวเตอร์" จะบอกคุณว่า

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

ตอนนี้มีการกล่าวว่ามีเทคนิคที่ชื่อพหุนาม Chebyshev สามารถใช้เพื่อให้ได้ผลลัพธ์ที่มีความแม่นยำเดียวกับพหุนามระดับต่ำ (เช่นตัวอย่างสำหรับโคไซน์ด้านบน) จากนั้นมีเทคนิคอื่น ๆ ที่จะสอดแทรกระหว่างค่าเพื่อเพิ่มความแม่นยำโดยไม่ต้องไปหาพหุนามที่มีขนาดใหญ่กว่าเช่น "วิธีกำหนดตารางความถูกต้องของแกลลอน" เทคนิคหลังนี้เป็นสิ่งที่โพสต์ที่อ้างถึงวรรณกรรม ACM หมายถึง แต่ในที่สุดพหุนาม Chebyshev เป็นสิ่งที่ใช้ในการรับ 90% ของวิธีการมี

สนุก.


6
ฉันไม่เห็นด้วยกับประโยคแรก ๆ นอกจากนี้ก็คุ้มค่าที่เรียกว่าคอมพิวเตอร์ฟังก์ชั่นพิเศษที่มีความแม่นยำรับประกันเป็นปัญหาหนัก คนที่ฉลาดที่คุณพูดถึงใช้เวลาส่วนใหญ่ในการทำสิ่งนี้ นอกจากนี้ในบันทึกทางเทคนิคเพิ่มเติมพหุนาม min-max เป็น graal ใฝหาและพหุนาม Chebyshev เป็นพร็อกซี่ง่ายสำหรับพวกเขา
Alexandre C.

161
-1 เสียงไม่เป็นมืออาชีพและการท่องเที่ยว (และหยาบคายอย่างอ่อนโยน) และความจริงที่ว่าไม่ซ้ำซ้อนที่เกิดขึ้นจริงเนื้อหาของคำตอบนี้ปล้นท่องเที่ยวและการยอมรับโดยทั่วไปเดือดลงไป "พวกเขามักจะใช้หลายชื่อเซฟดูหนังสือเล่มนี้ สำหรับรายละเอียดเพิ่มเติมมันดีจริงๆ! " ที่คุณรู้ว่าอาจจะถูกต้องอย่างแน่นอน แต่มันไม่ใช่คำตอบที่เรามีอยู่ในนั้น ย่อลงเช่นนั้นมันจะทำให้ความคิดเห็นที่ดีในคำถามแม้ว่า
Ilmari Karonen

2
ย้อนกลับไปในช่วงต้นปีของการพัฒนาเกมมันมักจะทำกับตารางการค้นหาที่จำเป็นสำหรับความเร็ว) โดยทั่วไปเราไม่ได้ใช้ฟังก์ชั่น lib มาตรฐานสำหรับสิ่งเหล่านั้น
topspin

4
ฉันใช้ตารางการค้นหาในระบบฝังตัวค่อนข้างบ่อยและ bittians (แทนเรเดียน) แต่นี่เป็นแอปพลิเคชันพิเศษ (เช่นเกมของคุณ) ผมคิดว่าคนที่มีความสนใจในวิธีการที่คคอมไพเลอร์คำนวณบาปสำหรับตัวเลขทศนิยม ....
โดนัลด์เมอเรย์

1
อ่า 50 ปีมาแล้ว ฉันเริ่มเล่นกับ Burroughs B220 กับซีรี่ส์ McLaren ฮาร์ดแวร์ CDC ต่อมาและ Motorola 68000 Arcsin ยุ่งเหยิง - ฉันเลือกความฉลาดของสองชื่อที่ประกอบด้วยหลายชื่อและพัฒนารหัสเพื่อหาค่าสัมประสิทธิ์ที่เหมาะสมที่สุด
Rick James

15

สำหรับsinโดยเฉพาะการใช้การขยายตัวของเทย์เลอร์จะทำให้คุณ:

sin (x): = x - x ^ 3/3! + x ^ 5/5! - x ^ 7/7! + ... (1)

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

float sin(float x)
{
  float res=0, pow=x, fact=1;
  for(int i=0; i<5; ++i)
  {
    res+=pow/fact;
    pow*=-1*x*x;
    fact*=(2*(i+1))*(2*(i+1)+1);
  }

  return res;
}

หมายเหตุ: (1) ใช้งานได้เนื่องจาก aproximation sin (x) = x สำหรับมุมเล็ก ๆ สำหรับมุมที่ใหญ่ขึ้นคุณต้องคำนวณคำศัพท์มากขึ้นเพื่อให้ได้ผลลัพธ์ที่ยอมรับได้ คุณสามารถใช้อาร์กิวเมนต์ while และดำเนินการต่อเพื่อความแม่นยำที่แน่นอน:

double sin (double x){
    int i = 1;
    double cur = x;
    double acc = 1;
    double fact= 1;
    double pow = x;
    while (fabs(acc) > .00000001 &&   i < 100){
        fact *= ((2*i)*(2*i+1));
        pow *= -1 * x*x; 
        acc =  pow / fact;
        cur += acc;
        i++;
    }
    return cur;

}

1
หากคุณปรับแต่งค่าสัมประสิทธิ์นิดหน่อย (และใส่รหัสลงในพหุนาม) คุณสามารถหยุดการทำซ้ำประมาณ 2 ครั้งได้เร็วขึ้น
Rick James

14

ใช่มีอัลกอริทึมซอฟต์แวร์สำหรับการคำนวณsinด้วย โดยทั่วไปแล้วการคำนวณสิ่งต่าง ๆ เหล่านี้กับคอมพิวเตอร์ดิจิตอลมักจะทำโดยใช้วิธีการเชิงตัวเลขเช่นประมาณซีรีย์เทย์เลอร์ที่เป็นตัวแทนของฟังก์ชั่น

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


12
การใช้งานจริงอาจจะไม่ใช้ซีรี่ส์ของ Taylor เนื่องจากมีวิธีที่มีประสิทธิภาพมากกว่า คุณจะต้องประมาณอย่างถูกต้องในโดเมน [0 ... pi / 2] และมีฟังก์ชั่นที่จะให้การประมาณที่ดีมีประสิทธิภาพมากกว่าซีรีย์ Taylor
David Thornley

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

1
อันที่จริงการประมาณพหุนามเป็นหนึ่งในวิธีที่มีประสิทธิภาพที่สุดในการคำนวณฟังก์ชันตรีโกณมิติ
Jeremy Salwen

13

ใช้ซีรี่ย์ของ Taylorและพยายามหาความสัมพันธ์ระหว่างเงื่อนไขของซีรีย์เพื่อที่คุณจะได้ไม่ต้องคำนวณอีกครั้ง

นี่คือตัวอย่างสำหรับ cosinus:

double cosinus(double x, double prec)
{
    double t, s ;
    int p;
    p = 0;
    s = 1.0;
    t = 1.0;
    while(fabs(t/s) > prec)
    {
        p++;
        t = (-t * x * x) / ((2 * p - 1) * (2 * p));
        s += t;
    }
    return s;
}

การใช้สิ่งนี้เราจะได้รับเทอมใหม่ของผลรวมโดยใช้คำที่ใช้ไปแล้ว (เราหลีกเลี่ยงแฟกทอเรียลและ x 2p )

คำอธิบาย


2
คุณรู้หรือไม่ว่าคุณสามารถใช้ Google Chart API เพื่อสร้างสูตรเช่นนี้โดยใช้ TeX code.google.com/apis/chart/docs/gallery/formulas.html
Gab Royer

11

มันเป็นคำถามที่ซับซ้อน ซีพียูที่เหมือน Intel ของตระกูล x86 มีการใช้งานฮาร์ดแวร์ของsin()ฟังก์ชั่น แต่เป็นส่วนหนึ่งของ x87 FPU และไม่ได้ใช้อีกต่อไปในโหมด 64 บิต (ที่ใช้รีจิสเตอร์ SSE2 แทน) ในโหมดนั้นจะมีการใช้งานซอฟต์แวร์

มีการนำไปใช้งานหลายอย่างเช่นนั้น หนึ่งอยู่ในfdlibmและใช้ใน Java เท่าที่ฉันรู้การติดตั้ง glibc นั้นมีส่วนต่างๆของ fdlibm และส่วนอื่น ๆ ที่ IBM สนับสนุน

การใช้งานซอฟต์แวร์ของฟังก์ชั่นที่ยอดเยี่ยมเช่นsin()โดยทั่วไปจะใช้การประมาณค่าโดยพหุนามซึ่งมักจะได้รับจากซีรีส์เทย์เลอร์


3
การลงทะเบียน SSE2 ไม่ได้ใช้ในการคำนวณ sin () ไม่ว่าในโหมด x86 หรือในโหมด x64 และแน่นอนว่า sin จะคำนวณในฮาร์ดแวร์โดยไม่คำนึงถึงโหมด เฮ้มันเป็นปี 2010 ที่เราอาศัยอยู่ :)
อิกอร์คอร์คอฟฮอฟ

7
@Igor: ขึ้นอยู่กับห้องสมุดคณิตศาสตร์ที่คุณกำลังดูอยู่ ปรากฎว่าห้องสมุดคณิตศาสตร์ที่ได้รับการปรับปรุงมากที่สุดใน x86 ใช้การใช้งานซอฟต์แวร์ SSE สำหรับsinและcosเร็วกว่าคำแนะนำฮาร์ดแวร์ใน FPU ห้องสมุดที่เรียบง่ายกว่าและไร้เดียงสามักใช้fsinและfcosคำแนะนำ
สตีเฟ่นแคนนอน

@Stephen Canon: ไลบรารีที่รวดเร็วเหล่านี้มีความแม่นยำ 80 บิตตามการลงทะเบียน FPU หรือไม่ ฉันสงสัยอย่างมากว่าพวกเขาชอบความเร็วมากกว่าความแม่นยำซึ่งแน่นอนว่ามีความสมเหตุสมผลในหลาย ๆ สถานการณ์เช่นในเกม และฉันเชื่อว่าการคำนวณไซน์ด้วยความแม่นยำ 32 บิตโดยใช้ SSE และตารางกลางที่คำนวณล่วงหน้าอาจเร็วกว่าการใช้FSINด้วยความแม่นยำเต็มรูปแบบ ฉันจะขอบคุณมากถ้าคุณบอกชื่อของห้องสมุดเร็วเหล่านั้นมันน่าสนใจที่ได้ดู
Igor Korkhov

@Igor: บน x86 ในโหมด 64 บิตอย่างน้อยในระบบแบบยูนิกซ์ที่ฉันรู้จักความแม่นยำนั้น จำกัด อยู่ที่ 64 บิตไม่ใช่ 79 บิตของ x87 FPU การใช้งานซอฟต์แวร์ของsin()เกิดขึ้นเร็วกว่าคอมพิวเตอร์ประมาณสองเท่าfsin(แม่นยำเพราะทำด้วยความแม่นยำน้อยกว่า) โปรดทราบว่า x87 นั้นมีความแม่นยำจริงน้อยกว่าการประกาศ 79 บิตเล็กน้อย
Thomas Pornin

1
แท้จริงแล้วการนำไปใช้งานทั้งแบบ 32- บิตและ 64- บิตของ sin () ในไลบรารีรันไทม์ msvc ไม่ได้ใช้คำสั่ง FSIN ในความเป็นจริงพวกเขาให้ผลลัพธ์ที่แตกต่างกันยกตัวอย่างบาป (0.70444454416678126) 0.64761068800896837 (ถูกต้องภายใน 0.5 * (eps / 2)) ในโปรแกรม 32 บิตและจะส่งผลให้ 0.64761068800896848 (ผิด) ใน 64 บิต
e.tadeu

9

ชื่อพหุนาม Chebyshev ดังที่ได้กล่าวไว้ในคำตอบอีกคำหนึ่งคือชื่อพหุนามที่ความแตกต่างที่ใหญ่ที่สุดระหว่างฟังก์ชันและพหุนามมีขนาดเล็กที่สุด นั่นคือการเริ่มต้นที่ยอดเยี่ยม

ในบางกรณีข้อผิดพลาดสูงสุดไม่ใช่สิ่งที่คุณสนใจ แต่เป็นข้อผิดพลาดสัมพัทธ์สูงสุด ตัวอย่างเช่นฟังก์ชัน sine ข้อผิดพลาดใกล้ x = 0 ควรน้อยกว่าค่าที่มากขึ้น คุณต้องการข้อผิดพลาดสัมพัทธ์เล็กน้อย ดังนั้นคุณจะคำนวณพหุนาม Chebyshev สำหรับ sin x / x, และคูณพหุนามนั้นด้วย x

ต่อไปคุณต้องหาวิธีการประเมินพหุนาม คุณต้องการประเมินค่าในลักษณะที่ค่ากลางมีขนาดเล็กดังนั้นข้อผิดพลาดในการปัดเศษจึงมีขนาดเล็ก มิฉะนั้นข้อผิดพลาดในการปัดเศษอาจใหญ่กว่าข้อผิดพลาดในพหุนาม และด้วยฟังก์ชั่นเช่นฟังก์ชันไซน์หากคุณไม่ระมัดระวังก็อาจเป็นไปได้ว่าผลลัพธ์ที่คุณคำนวณสำหรับบาป x นั้นมากกว่าผลของบาป y แม้ในขณะที่ x <y ดังนั้นควรเลือกตัวเลือกลำดับการคำนวณและการคำนวณขอบเขตบนสำหรับข้อผิดพลาดในการปัดเศษอย่างระมัดระวัง

ตัวอย่างเช่น sin x = x - x ^ 3/6 + x ^ 5/120 - x ^ 7/5040 ... หากคุณคำนวณ naively sin x = x * (1 - x ^ 2/6 + x ^ 4 / 120 - x ^ 6/5040 ... ) จากนั้นฟังก์ชั่นในวงเล็บจะลดลงและมันจะเกิดขึ้นว่าถ้า y เป็นจำนวนที่มากกว่าถัดไปเป็น x บางครั้งบาป y จะเล็กกว่าบาป x ให้คำนวณ sin x = x - x ^ 3 * (1/6 - x ^ 2/120 + x ^ 4/5040 ... ) ซึ่งสิ่งนี้ไม่สามารถเกิดขึ้นได้

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

ตัวอย่างเช่น sin (x) ซึ่งคุณต้องการสัมประสิทธิ์สำหรับ x, x ^ 3, x ^ 5, x ^ 7 เป็นต้นคุณทำสิ่งต่อไปนี้: คำนวณการประมาณที่ดีที่สุดของ sin x ด้วยพหุนาม (ax + bx ^ 3 + cx ^ 5 + dx ^ 7) ที่มีความแม่นยำสูงกว่าสองเท่าจากนั้นปัด a ถึงความแม่นยำสองเท่าทำให้ A. ความแตกต่างระหว่าง a และ A นั้นค่อนข้างใหญ่ ตอนนี้คำนวณการประมาณที่ดีที่สุดของ (sin x - Ax) ด้วยพหุนาม (bx ^ 3 + cx ^ 5 + dx ^ 7) คุณจะได้ค่าสัมประสิทธิ์ที่แตกต่างกันเพราะพวกมันปรับให้เข้ากับความแตกต่างระหว่าง a และ A รอบ b ถึงความแม่นยำสองเท่าจากนั้นค่าประมาณ (sin x - Ax - Bx ^ 3) ด้วยพหุนาม cx ^ 5 + dx ^ 7 เป็นต้น คุณจะได้พหุนามที่เกือบดีพอ ๆ กับพหุนาม Chebyshev ดั้งเดิม แต่ดีกว่า Chebyshev ปัดขึ้นเป็นสองเท่าอย่างแม่นยำ

ถัดไปคุณควรคำนึงถึงข้อผิดพลาดในการปัดเศษในการเลือกพหุนาม คุณพบพหุนามที่มีข้อผิดพลาดขั้นต่ำในข้อผิดพลาดการปัดเศษพหุนาม แต่คุณต้องการเพิ่มประสิทธิภาพข้อผิดพลาดพหุนามบวกและการปัดเศษ เมื่อคุณมีพหุนาม Chebyshev คุณสามารถคำนวณขอบเขตสำหรับข้อผิดพลาดในการปัดเศษ สมมติว่า f (x) เป็นฟังก์ชันของคุณ P (x) คือพหุนามและ E (x) เป็นข้อผิดพลาดในการปัดเศษ คุณไม่ต้องการเพิ่มประสิทธิภาพ | f (x) - P (x) |, คุณต้องการเพิ่มประสิทธิภาพ | f (x) - P (x) +/- E (x) | คุณจะได้รับพหุนามที่แตกต่างกันเล็กน้อยซึ่งพยายามที่จะทำให้ข้อผิดพลาดพหุนามลดลงซึ่งข้อผิดพลาดในการปัดเศษมีขนาดใหญ่

ทั้งหมดนี้จะทำให้คุณปัดเศษข้อผิดพลาดได้อย่างง่ายดายมากที่สุด 0.55 เท่าของบิตสุดท้ายโดยที่ +, -, *, / มีข้อผิดพลาดในการปัดเศษสูงสุด 0.50 เท่าของบิตสุดท้าย


1
นี่คือคำอธิบายที่ดีของวิธีการหนึ่งที่อาจคำนวณบาป (x) ได้อย่างมีประสิทธิภาพ แต่มันไม่ได้จริงๆดูเหมือนจะตอบคำถามของ OP ซึ่งเป็นเฉพาะเกี่ยวกับวิธีการที่พบบ่อยห้องสมุด C / คอมไพเลอร์จะคำนวณ
Ilmari Karonen

ชื่อพหุนาม Chebyshev ลดค่าสัมบูรณ์สัมบูรณ์สูงสุดในช่วงเวลาหนึ่ง แต่จะไม่ลดความแตกต่างที่ใหญ่ที่สุดระหว่างฟังก์ชันเป้าหมายและพหุนาม Minimax polynomials ทำอย่างนั้น
Eric Postpischil

9

เกี่ยวกับฟังก์ชั่นตรีโกณมิติเช่นsin(), cos(), tan()ยังไม่มีการพูดถึงหลังจาก 5 ปีของการเป็นสิ่งสำคัญของฟังก์ชั่นที่มีคุณภาพสูงหนุน: ลดช่วง

ขั้นตอนแรก ๆ ของฟังก์ชั่นเหล่านี้คือการลดมุมในหน่วยเรเดียนให้อยู่ในช่วงของช่วง 2 * π แต่πนั้นไม่มีเหตุผลดังนั้นการลดลงอย่างง่ายเช่นx = remainder(x, 2*M_PI)ข้อผิดพลาดในการแนะนำM_PIหรือ machine pi เป็นการประมาณของπ ดังนั้นจะทำx = remainder(x, 2*π)อย่างไร?

ห้องสมุดในช่วงต้นใช้ความแม่นยำการขยายหรือการเขียนโปรแกรมที่สร้างขึ้นเพื่อให้ผลลัพธ์ที่มีคุณภาพ doubleแต่ยังคงในช่วงที่จำนวน เมื่อมีการร้องขอค่าขนาดใหญ่sin(pow(2,30))ผลลัพธ์จะไร้ความหมายหรือ0.0อาจตั้งค่าสถานะข้อผิดพลาดเป็นบางสิ่งเช่นTLOSSการสูญเสียความแม่นยำทั้งหมดหรือPLOSSการสูญเสียความแม่นยำบางส่วน

การลดช่วงที่ดีของค่าขนาดใหญ่ไปยังช่วงเวลาเช่น-πถึงπเป็นปัญหาที่ท้าทายซึ่งแข่งขันกับความท้าทายของฟังก์ชันตรีโกณฯ พื้นฐานเช่นsin()ตัวของมันเอง

รายงานที่ดีคือการลดอาร์กิวเมนต์สำหรับอาร์กิวเมนต์จำนวนมาก: ดีถึงบิตสุดท้าย (1992) มันครอบคลุมปัญหากัน: กล่าวถึงความจำเป็นและว่าสิ่งที่อยู่บนแพลตฟอร์มต่างๆ (SPARC, PC, HP, 30 + อื่น ๆ ) และยังมีขั้นตอนวิธีการแก้ปัญหาให้ผลลัพธ์ที่มีคุณภาพสำหรับทุกคน doubleจากไป-DBL_MAXDBL_MAX


หากอาร์กิวเมนต์ดั้งเดิมมีหน่วยเป็นองศา แต่อาจมีค่ามากให้ใช้fmod()ก่อนเพื่อความแม่นยำที่ดีขึ้น ดีfmod()จะไม่มีข้อผิดพลาดและให้ลดช่วงที่ดีเยี่ยม

// sin(degrees2radians(x))
sin(degrees2radians(fmod(x, 360.0))); // -360.0 < fmod(x,360) < +360.0

ตรีโกณมิติต่างๆและremquo()เสนอการปรับปรุงให้ดียิ่งขึ้น ตัวอย่าง: sind ()


6

การใช้งานจริงของฟังก์ชันห้องสมุดขึ้นอยู่กับผู้รวบรวมและ / หรือผู้ให้บริการห้องสมุดเฉพาะ ไม่ว่าจะทำในฮาร์ดแวร์หรือซอฟต์แวร์ไม่ว่าจะเป็นการขยายตัวของเทย์เลอร์หรือไม่เป็นต้นจะแตกต่างกันไป

ฉันรู้ว่านั่นไม่ช่วยอะไรเลย


5

โดยทั่วไปจะใช้งานในซอฟต์แวร์และจะไม่ใช้การเรียกฮาร์ดแวร์ (นั่นคือ aseembly) ที่เกี่ยวข้องในกรณีส่วนใหญ่ อย่างไรก็ตามเจสันชี้ให้เห็นว่าสิ่งเหล่านี้เป็นการนำไปปฏิบัติโดยเฉพาะ

โปรดทราบว่ารูทีนซอฟต์แวร์เหล่านี้ไม่ได้เป็นส่วนหนึ่งของซอร์สคอมไพเลอร์ แต่จะพบได้ในไลบรารีที่สัมพันธ์กันเช่น clib หรือ glibc สำหรับคอมไพเลอร์ GNU ดูhttp://www.gnu.org/software/libc/manual/html_mono/libc.html#Trig-Functions

หากคุณต้องการการควบคุมที่มากขึ้นคุณควรประเมินสิ่งที่คุณต้องการอย่างถี่ถ้วน วิธีการทั่วไปบางอย่างเป็นการแก้ไขของตารางการค้นหา, การเรียกการชุมนุม (ซึ่งมักจะช้า) หรือแผนการประมาณอื่น ๆ เช่น Newton-Raphson สำหรับรากที่สอง


5

หากคุณต้องการการใช้งานซอฟต์แวร์ที่ไม่ฮาร์ดแวร์สถานที่ที่จะมองหาคำตอบที่ชัดเจนกับคำถามนี้เป็นบทที่ 5 ของตัวเลขสูตร สำเนาของฉันอยู่ในกล่องดังนั้นฉันไม่สามารถให้รายละเอียดได้ แต่รุ่นสั้น ๆ (ถ้าฉันจำได้ถูกต้อง) คือคุณทำtan(theta/2)ตามการดำเนินการดั้งเดิมของคุณและคำนวณผู้อื่นจากที่นั่น การคำนวณจะทำกับประมาณชุด แต่มันเป็นสิ่งที่ลู่มากขึ้นอย่างรวดเร็วกว่าซีรีส์เทย์เลอร์

ขออภัยฉันไม่สามารถจดจำได้มากขึ้นหากไม่ได้รับหนังสือ


5

ไม่มีอะไรที่เหมือนกับการกดปุ่มแหล่งที่มาและดูว่ามีใครบางคนทำมันจริงในห้องสมุดในการใช้งานทั่วไป; มาดูการใช้งาน C หนึ่งไลบรารี่โดยเฉพาะ ฉันเลือก uLibC

นี่คือฟังก์ชั่นบาป:

http://git.uclibc.org/uClibc/tree/libm/s_sin.c

ซึ่งดูเหมือนว่าจะจัดการกับกรณีพิเศษไม่กี่กรณีและจากนั้นทำการลดการโต้แย้งเพื่อแมปอินพุตไปยังช่วง [-pi / 4, pi / 4], (แยกการโต้แย้งออกเป็นสองส่วนส่วนใหญ่และหาง) ก่อนโทร

http://git.uclibc.org/uClibc/tree/libm/k_sin.c

ซึ่งทำงานกับสองส่วนนั้น หากไม่มีหางคำตอบโดยประมาณจะถูกสร้างขึ้นโดยใช้พหุนามของระดับ 13 หากมีหางคุณจะได้รับการแก้ไขเพิ่มเติมเล็กน้อยตามหลักการที่ว่าsin(x+y) = sin(x) + sin'(x')y


4

เมื่อใดก็ตามที่ฟังก์ชั่นดังกล่าวได้รับการประเมินแล้วในบางระดับก็มีโอกาสมากที่สุดเช่นกัน:

  • ตารางค่าที่สอดแทรก (สำหรับแอปพลิเคชันที่รวดเร็วและไม่แม่นยำเช่นกราฟิกคอมพิวเตอร์)
  • การประเมินผลของซีรีส์ที่รวมเข้ากับค่าที่ต้องการ --- อาจไม่ใช่ซีรีย์เทย์เลอร์มีแนวโน้มที่จะมีอะไรบางอย่างขึ้นอยู่กับการสร้างพื้นที่สี่เหลี่ยมจัตุรัสเช่น Clenshaw-Curtis

หากไม่มีการสนับสนุนด้านฮาร์ดแวร์คอมไพเลอร์อาจใช้วิธีการหลังปล่อยรหัสแอสเซมเบลอร์เท่านั้น (โดยไม่มีสัญลักษณ์การดีบัก) แทนที่จะใช้ไลบรารี ac - ทำให้ยุ่งยากในการติดตามรหัสจริงในดีบักเกอร์ของคุณ


4

ดังที่หลายคนชี้ให้เห็น แต่เท่าที่ฉันเข้าใจคำถามของคุณคุณมีความสนใจในซอฟต์แวร์จริงที่ทำให้ฟังก์ชั่นคณิตศาสตร์ แต่ไม่สามารถหามันเจอได้ หากเป็นกรณีนี้คุณอยู่ที่นี่:

  • ดาวน์โหลดซอร์สโค้ด glibc จากhttp://ftp.gnu.org/gnu/glibc/
  • ดูไฟล์ที่dosincos.cอยู่ในโฟลเดอร์glibc root \ sysdeps \ ieee754 \ dbl-64 ที่ยังไม่ได้บรรจุ
  • ในทำนองเดียวกันคุณสามารถค้นหาการใช้งานของส่วนที่เหลือของห้องสมุดคณิตศาสตร์เพียงมองหาไฟล์ที่มีชื่อที่เหมาะสม

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

ฉันหวังว่านี่จะช่วยได้.


4

ฉันจะลองตอบคำถามsin()ในโปรแกรม C คอมไพล์ด้วยคอมไพเลอร์ C ของ GCC ในโปรเซสเซอร์ x86 ปัจจุบัน (สมมติว่าเป็น Intel Core 2 Duo)

ในภาษา C มาตรฐาน C Library รวมถึงฟังก์ชั่นทางคณิตศาสตร์ทั่วไปไม่รวมอยู่ในภาษาตัวเอง (เช่นpow, sinและcosอำนาจไซน์และโคไซน์ตามลำดับ) ส่วนหัวของซึ่งรวมอยู่ในmath.h

ขณะนี้ในระบบ GNU / Linux ฟังก์ชันไลบรารีเหล่านี้มีให้โดย glibc (GNU libc หรือ GNU C Library) แต่คอมไพเลอร์ GCC ต้องการให้คุณเชื่อมโยงไปยังห้องสมุดคณิตศาสตร์ ( libm.so) โดยใช้-lmธงคอมไพเลอร์เพื่อเปิดใช้งานฟังก์ชั่นทางคณิตศาสตร์เหล่านี้ ฉันไม่แน่ใจว่าทำไมมันไม่ได้เป็นส่วนหนึ่งของไลบรารี C มาตรฐาน สิ่งเหล่านี้จะเป็นเวอร์ชันซอฟต์แวร์ของฟังก์ชันเลขทศนิยมหรือ "soft-float"

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

ตอนนี้คอมไพเลอร์อาจปรับฟังก์ชั่นไลบรารี C มาตรฐานsin()(ให้บริการโดยlibm.so) เพื่อแทนที่ด้วยการเรียกการเรียนการสอนพื้นเมืองไปยังฟังก์ชั่นบาปในตัว CPU / FPU ของคุณซึ่งมีอยู่เป็นคำสั่ง FPU ( FSINสำหรับ x86 / x87) โปรเซสเซอร์รุ่นใหม่เช่นซีรีย์ Core 2 (นี่ค่อนข้างถูกต้องเมื่อเทียบกับ i486DX) สิ่งนี้จะขึ้นอยู่กับแฟล็กการปรับให้เหมาะสมที่ส่งไปยังคอมไพเลอร์ gcc หากคอมไพเลอร์ได้รับคำสั่งให้เขียนรหัสที่จะดำเนินการกับ i386 หรือโปรเซสเซอร์รุ่นใหม่ใด ๆ ก็จะไม่ทำให้การเพิ่มประสิทธิภาพดังกล่าว การ-mcpu=486ตั้งค่าสถานะจะแจ้งให้คอมไพเลอร์ทราบว่าการเพิ่มประสิทธิภาพดังกล่าวปลอดภัยแล้ว

ตอนนี้หากโปรแกรมดำเนินการฟังก์ชัน sin () เวอร์ชันซอฟต์แวร์ก็จะทำตามพื้นฐานของCORDIC (COordinate Rotation DIgital Computer) หรืออัลกอริทึม BKMหรือมีแนวโน้มที่จะคำนวณตารางหรืออนุกรมกำลังซึ่งตอนนี้ใช้กันทั่วไปในการคำนวณ ฟังก์ชั่นยอดเยี่ยมดังกล่าว [Src: http://en.wikipedia.org/wiki/Cordic#Application]

gcc รุ่นล่าสุด (ตั้งแต่ 2.9x โดยประมาณ) ใด ๆ ยังมีบาปรุ่นในตัวด้วย__builtin_sin()ซึ่งจะใช้เพื่อแทนที่การเรียกมาตรฐานไปเป็นเวอร์ชันไลบรารี C เพื่อเป็นการเพิ่มประสิทธิภาพ

ฉันแน่ใจว่าชัดเจนเหมือนโคลน แต่หวังว่าจะให้ข้อมูลมากกว่าที่คุณคาดหวังไว้และกระโดดลงจากจุดเพื่อเรียนรู้เพิ่มเติมด้วยตัวคุณเอง


3

หากคุณต้องการดูการใช้งาน GNU จริงของฟังก์ชั่นเหล่านั้นใน C ให้ตรวจสอบ trunk ล่าสุดของ glibc ดูC Library


3

อย่าใช้ซีรี่ส์ของ Taylor ชื่อพหุนาม Chebyshev มีความรวดเร็วและแม่นยำยิ่งขึ้นดังที่คนสองคนข้างต้นชี้ให้เห็น นี่คือการดำเนินการ (มาจาก ZX Spectrum ROM): https://albertveli.wordpress.com/2015/01/10/zx-sine/


2
ดูเหมือนจะไม่ตอบคำถามตามที่ถามจริง ๆ สหกรณ์จะขอฟังก์ชั่นวิธีการหนุนกำลังคำนวณได้จากคอมไพเลอร์ C ทั่วไป / ห้องสมุด (และผมค่อนข้างมั่นใจว่า ZX สเปกตรัมไม่ได้มีคุณสมบัติ) ไม่ใช่วิธีที่พวกเขาควรจะได้รับการคำนวณ นี่อาจเป็นความคิดเห็นที่มีประโยชน์สำหรับบางคำตอบก่อนหน้านี้
Ilmari Karonen

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

2

การคำนวณไซน์ / โคไซน์ / แทนเจนต์เป็นเรื่องง่ายมากที่จะทำผ่านรหัสโดยใช้ซีรีย์ของเทย์เลอร์ การเขียนด้วยตัวเองใช้เวลาประมาณ 5 วินาที

กระบวนการทั้งหมดสามารถสรุปได้ด้วยสมการนี้:

บาปและการขยายตัวของต้นทุน

นี่คือกิจวัตรบางส่วนที่ฉันเขียนให้ C:

double _pow(double a, double b) {
    double c = 1;
    for (int i=0; i<b; i++)
        c *= a;
    return c;
}

double _fact(double x) {
    double ret = 1;
    for (int i=1; i<=x; i++) 
        ret *= i;
    return ret;
}

double _sin(double x) {
    double y = x;
    double s = -1;
    for (int i=3; i<=100; i+=2) {
        y+=s*(_pow(x,i)/_fact(i));
        s *= -1;
    }  
    return y;
}
double _cos(double x) {
    double y = 1;
    double s = -1;
    for (int i=2; i<=100; i+=2) {
        y+=s*(_pow(x,i)/_fact(i));
        s *= -1;
    }  
    return y;
}
double _tan(double x) {
     return (_sin(x)/_cos(x));  
}

4
นี่เป็นการดำเนินการที่ค่อนข้างแย่เนื่องจากไม่ได้ใช้ว่าคำที่ต่อเนื่องของไซน์และอนุกรมโคไซน์นั้นมีความฉลาดง่ายมาก ซึ่งหมายความว่าเราสามารถลดจำนวนการคูณและการหารจาก O (n ^ 2) ที่นี่เป็น O (n) การลดลงเพิ่มเติมทำได้โดยแบ่งครึ่งและยกกำลังสองเช่นในไลบรารีคณิตศาสตร์ bc (POSIX multiprecision calculator)
Lutz Lehmann

2
ดูเหมือนว่าจะไม่ตอบคำถามตามที่ถาม OP กำลังถามวิธีคำนวณฟังก์ชันของคอมไพเลอร์ / ไลบรารี C ทั่วไปไม่ใช่สำหรับการปรับใช้แบบกำหนดเองใหม่
Ilmari Karonen

2
ฉันคิดว่ามันเป็นคำตอบที่ดีเพราะมันตอบจิตวิญญาณของคำถามที่ (และฉันสามารถเดาได้แน่นอน) อยากรู้อยากเห็นเกี่ยวกับฟังก์ชั่น "กล่องดำ" อย่างเช่นบาป () มันเป็นคำตอบเดียวที่นี่ที่ให้โอกาสหนึ่งในการทำความเข้าใจสิ่งที่เกิดขึ้นอย่างรวดเร็วโดยการขัดมันภายในไม่กี่วินาทีแทนที่จะอ่านซอร์สโค้ด C ที่เพิ่มประสิทธิภาพ
Mike M

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


0

ปรับปรุงโค้ดรุ่นจากคำตอบของ Blindy

#define EPSILON .0000000000001
// this is smallest effective threshold, at least on my OS (WSL ubuntu 18)
// possibly because factorial part turns 0 at some point
// and it happens faster then series element turns 0;
// validation was made against sin() from <math.h>
double ft_sin(double x)
{
    int k = 2;
    double r = x;
    double acc = 1;
    double den = 1;
    double num = x;

//  precision drops rapidly when x is not close to 0
//  so move x to 0 as close as possible
    while (x > PI)
        x -= PI;
    while (x < -PI)
        x += PI;
    if (x > PI / 2)
        return (ft_sin(PI - x));
    if (x < -PI / 2)
        return (ft_sin(-PI - x));
//  not using fabs for performance reasons
    while (acc > EPSILON || acc < -EPSILON)
    {
        num *= -x * x;
        den *= k * (k + 1);
        acc = num / den;
        r += acc;
        k += 2;
    }
    return (r);
}

0

สาระสำคัญของการทำสิ่งนี้อยู่ในส่วนที่ตัดตอนมาจากการวิเคราะห์เชิงตัวเลขประยุกต์โดย Gerald Wheatley:

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

จุดสองสามข้อที่กล่าวถึงข้างต้นคืออัลกอริธึมบางตัวทำการประมาณค่าแบบ Infact จากตารางแม้ว่าจะเป็นเพียงการทำซ้ำสองสามครั้งแรกเท่านั้น นอกจากนี้โปรดทราบว่ามันระบุว่าคอมพิวเตอร์ใช้พหุนามประมาณโดยไม่ระบุประเภทพหุนามประมาณ ดังที่คนอื่น ๆ ในกระทู้ได้ชี้ให้เห็นชื่อพหุนาม Chebyshev มีประสิทธิภาพมากกว่าพหุนามเทย์เลอร์ในกรณีนี้


-1

ถ้าคุณต้องการsinแล้ว

 __asm__ __volatile__("fsin" : "=t"(vsin) : "0"(xrads));

ถ้าคุณต้องการcosแล้ว

 __asm__ __volatile__("fcos" : "=t"(vcos) : "0"(xrads));

ถ้าคุณต้องการsqrtแล้ว

 __asm__ __volatile__("fsqrt" : "=t"(vsqrt) : "0"(value));

เหตุใดจึงต้องใช้รหัสที่ไม่ถูกต้องเมื่อคำแนะนำของเครื่องจะทำอย่างไร


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