วิธีที่เร็วที่สุดในการคำนวณ sin และ cos เข้าด้วยกันคืออะไร?


100

ฉันต้องการคำนวณทั้งไซน์และไซน์ร่วมของค่าด้วยกัน (ตัวอย่างเช่นการสร้างเมทริกซ์การหมุน) แน่นอนว่าฉันสามารถคำนวณแยกจากกันได้a = cos(x); b = sin(x);แต่ฉันสงสัยว่ามีวิธีที่เร็วกว่าเมื่อต้องการทั้งสองค่า

แก้ไข: เพื่อสรุปคำตอบจนถึงตอนนี้:

  • วลาดกล่าวว่ามีคำสั่ง asm ที่FSINCOSคำนวณทั้งคู่ (เกือบจะในเวลาเดียวกันกับการโทรไปหาFSINคนเดียว)

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

  • คาเฟ่ชี้ให้เห็นว่าฟังก์ชั่นsincosและsincosfอาจพร้อมใช้งานและสามารถเรียกได้โดยตรงเพียงแค่รวมmath.h

  • มีการกล่าวถึงวิธีการของ Tanascius ในการใช้ตารางการค้นหาที่ขัดแย้งกัน (อย่างไรก็ตามในคอมพิวเตอร์ของฉันและในสถานการณ์มาตรฐานจะทำงานได้เร็วกว่า 3 เท่าsincosโดยมีความแม่นยำเกือบเท่ากันสำหรับจุดลอยตัว 32 บิต)

  • โจเอลกู๊ดวินเชื่อมโยงกับแนวทางที่น่าสนใจของเทคนิคการประมาณค่าที่รวดเร็วมากพร้อมด้วยความแม่นยำที่ค่อนข้างดี (สำหรับฉันนี่จะเร็วกว่านี้จากการค้นหาตาราง)


1
ดูคำถามเกี่ยวกับการนำ sin / cos มาใช้: stackoverflow.com/questions/1640595
Joel Goodwin

1
ลองsinx ~ x-x^3/6และcosx~1-x^2/4เป็นประมาณถ้าคุณดูแลเกี่ยวกับความเร็วมากกว่าความถูกต้อง คุณสามารถเพิ่มคำศัพท์ในอนุกรมใดก็ได้เมื่อคุณเพิ่มน้ำหนักให้กับความแม่นยำมากขึ้น ( en.wikipedia.org/wiki/Taylor_seriesเลื่อนลงไปที่ทริกเกอร์ชุดเทย์เลอร์) โปรดทราบว่านี่เป็นวิธีทั่วไปในการประมาณฟังก์ชันที่คุณต้องการซึ่งเป็นnเวลาที่แตกต่างกัน ดังนั้นถ้าคุณมีฟังก์ชันที่ใหญ่กว่าซึ่งของไซน์และโคไซน์เป็นของคุณจะได้ความเร็วเพิ่มขึ้นมากถ้าคุณประมาณมันแทนที่จะเป็นบาป cos ก็แยกกัน
ldog

นี่เป็นเทคนิคที่ไม่ดีและมีความแม่นยำต่ำมาก ดูโพสต์โดย Joel Goodwin ชุดเทย์เลอร์ได้รับการโพสต์ไว้ด้านล่าง กรุณาโพสต์เป็นคำตอบ
Danvil

1
ขึ้นอยู่กับความต้องการของคุณหากคุณต้องการความแม่นยำของชุดเทย์เลอร์จะเป็นการประมาณที่ดีก็ต่อเมื่อคุณต้องการค่าxใกล้เคียงกับบางจุดx_0จากนั้นขยายชุดเทย์เลอร์ของคุณไปรอบ ๆx_0แทน 0 ซึ่งจะให้ความแม่นยำที่ยอดเยี่ยมใกล้x_0แต่ยิ่งไกลออกไป ผลลัพธ์จะยิ่งแย่ลง คุณอาจจะคิดว่าถูกต้องครับสาเหตุที่คุณมองไปที่ asnwer 0ที่กำหนดและพยายามมันสำหรับค่าที่ห่างไกลจาก คำตอบนั้นมาจากบาป cos ขยายประมาณ 0
ldog

คำตอบ:


52

โปรเซสเซอร์ Intel / AMD สมัยใหม่มีคำสั่งFSINCOSสำหรับการคำนวณฟังก์ชันไซน์และโคไซน์พร้อมกัน หากคุณต้องการการเพิ่มประสิทธิภาพที่แข็งแกร่งคุณควรใช้มัน

นี่คือตัวอย่างเล็ก ๆ : http://home.broadpark.no/~alein/fsincos.html

นี่คืออีกตัวอย่างหนึ่ง (สำหรับ MSVC): http://www.codeguru.com/forum/showthread.php?t=328669

นี่เป็นอีกตัวอย่างหนึ่ง (พร้อม gcc): http://www.allegro.cc/forums/thread/588470

หวังว่าหนึ่งในนั้นจะช่วยได้ (ฉันไม่ได้ใช้คำสั่งนี้ด้วยตัวเองขออภัย)

เนื่องจากได้รับการสนับสนุนในระดับโปรเซสเซอร์ฉันคาดว่าพวกเขาจะเร็วกว่าการค้นหาตารางมาก

แก้ไข:
Wikipediaแนะนำว่าFSINCOSมีการเพิ่มโปรเซสเซอร์ 387 ดังนั้นคุณแทบจะไม่พบโปรเซสเซอร์ที่ไม่รองรับ

แก้ไข:
เอกสารของ Intelระบุว่าFSINCOSช้ากว่าประมาณ 5 เท่าFDIV(เช่นการแบ่งจุดลอยตัว)

แก้ไข:
โปรดทราบว่าคอมไพเลอร์สมัยใหม่บางตัวไม่ได้ปรับการคำนวณไซน์และโคไซน์ให้เหมาะสมที่สุดในการเรียกFSINCOSใช้ โดยเฉพาะ VS 2008 ของฉันไม่ได้ทำแบบนั้น

แก้ไข:
การเชื่อมโยงตัวอย่างแรกจะตาย แต่มียังคงเป็นรุ่นที่เครื่อง Wayback


1
@phkahler: จะดีมาก ไม่รู้ว่าคอมไพเลอร์สมัยใหม่ใช้การเพิ่มประสิทธิภาพดังกล่าวหรือไม่
Vlad

12
fsincosคำแนะนำคือไม่ "ค่อนข้างเร็ว" คู่มือการปรับแต่งการปรับให้เหมาะสมของ Intel ระบุว่าต้องใช้ระหว่าง 119 ถึง 250 รอบสำหรับสถาปัตยกรรมขนาดเล็กล่าสุด ไลบรารีคณิตศาสตร์ของ Intel (แจกจ่ายด้วย ICC) โดยการเปรียบเทียบสามารถคำนวณแยกกันsinและใช้cosเวลาน้อยกว่า 100 รอบโดยใช้การใช้งานซอฟต์แวร์ที่ใช้ SSE แทนหน่วย x87 การใช้งานซอฟต์แวร์ที่คล้ายกันซึ่งคำนวณทั้งสองอย่างพร้อมกันอาจทำได้เร็วกว่า
Stephen Canon

2
@ วลาด: ห้องสมุดคณิตศาสตร์ของ ICC ไม่ใช่โอเพ่นซอร์สและฉันไม่มีใบอนุญาตในการแจกจ่ายซ้ำดังนั้นฉันจึงไม่สามารถโพสต์แอสเซมบลีได้ ฉันบอกคุณได้ว่าไม่มีsinการคำนวณในตัวให้พวกเขาใช้ประโยชน์ได้อย่างไรก็ตาม พวกเขาใช้คำแนะนำ SSE เดียวกันกับคนอื่น ๆ สำหรับความคิดเห็นที่ 2 ของคุณความเร็วที่สัมพันธ์กับfdivไม่เป็นสาระสำคัญ หากมีสองวิธีในการทำบางสิ่งและวิธีหนึ่งเร็วกว่าอีกสองเท่าก็ไม่สมเหตุสมผลที่จะเรียกวิธีที่ช้ากว่าว่า "เร็ว" ไม่ว่าจะใช้เวลานานแค่ไหนเมื่อเทียบกับงานที่ไม่เกี่ยวข้องกันโดยสิ้นเชิง
Stephen Canon

1
sinฟังก์ชันซอฟต์แวร์ในไลบรารีให้ความแม่นยำสองเท่าเต็มรูปแบบ fsincosการเรียนการสอนมีความถูกต้องค่อนข้างมาก (ขยายคู่) แต่ที่ถูกต้องแม่นยำเป็นพิเศษที่ได้รับการโยนทิ้งไปในโปรแกรมส่วนใหญ่ที่เรียกsinฟังก์ชั่นเป็นผลของมันมักจะโค้งมนเพื่อความแม่นยำสองโดยดำเนินการทางคณิตศาสตร์ในภายหลังหรือร้านค้าในหน่วยความจำ ในสถานการณ์ส่วนใหญ่จะให้ความแม่นยำเดียวกันสำหรับการใช้งานจริง
Stephen Canon

4
โปรดทราบด้วยว่าfsincosไม่ใช่การนำไปใช้งานที่สมบูรณ์ด้วยตัวเอง คุณต้องมีขั้นตอนการลดช่วงเพิ่มเติมเพื่อใส่อาร์กิวเมนต์ลงในช่วงอินพุตที่ถูกต้องสำหรับfsincosคำสั่ง ไลบรารีsinและcosฟังก์ชันรวมถึงการลดลงนี้เช่นเดียวกับการคำนวณหลักดังนั้นจึงเร็วกว่า (โดยการเปรียบเทียบ) ยิ่งกว่าการกำหนดเวลารอบที่ฉันระบุไว้
Stephen Canon

39

โปรเซสเซอร์ x86 ที่ทันสมัยมีคำสั่ง fsincos ซึ่งจะทำตามที่คุณต้องการ - คำนวณ sin และ cos ในเวลาเดียวกัน คอมไพเลอร์การเพิ่มประสิทธิภาพที่ดีควรตรวจจับโค้ดที่คำนวณค่า sin และ cos สำหรับค่าเดียวกันและใช้คำสั่ง fsincos เพื่อดำเนินการนี้

แฟล็กคอมไพเลอร์ต้องใช้เวลาสองสามครั้งเพื่อให้สิ่งนี้ทำงานได้ แต่:

$ gcc --version
i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5488)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ cat main.c
#include <math.h> 

struct Sin_cos {double sin; double cos;};

struct Sin_cos fsincos(double val) {
  struct Sin_cos r;
  r.sin = sin(val);
  r.cos = cos(val);
  return r;
}

$ gcc -c -S -O3 -ffast-math -mfpmath=387 main.c -o main.s

$ cat main.s
    .text
    .align 4,0x90
.globl _fsincos
_fsincos:
    pushl   %ebp
    movl    %esp, %ebp
    fldl    12(%ebp)
    fsincos
    movl    8(%ebp), %eax
    fstpl   8(%eax)
    fstpl   (%eax)
    leave
    ret $4
    .subsections_via_symbols

Tada มันใช้คำสั่ง fsincos!


ที่นี่หนาว! คุณช่วยอธิบายได้ไหมว่า -mfpmath = 387 กำลังทำอะไรอยู่ และยังใช้ได้กับ MSVC หรือไม่?
Danvil

1
สังเกตว่า-ffast-mathและ-mfpmathนำไปสู่ผลลัพธ์ที่แตกต่างกันในบางกรณี
Debilski

3
mfpmath = 387 จะบังคับให้ gcc ใช้คำสั่ง x87 แทนคำแนะนำ SSE ฉันสงสัยว่า MSVC มีการเพิ่มประสิทธิภาพและแฟล็กที่คล้ายกัน แต่ฉันไม่มี MSVC ที่สะดวกเพื่อให้แน่ใจ การใช้คำสั่ง x87 อาจเป็นอันตรายต่อประสิทธิภาพในโค้ดอื่น ๆ แต่คุณควรดูคำตอบอื่นของฉันเพื่อใช้ MKL ของ Intel
จิ

gcc 3.4.4 เก่าของฉันจาก cygwin สร้างสายเรียกเข้าfsinและfcos. :-(
Vlad

พยายามกับ Visual Studio 2008 ที่เปิดใช้งานการเพิ่มประสิทธิภาพสูงสุด เรียกใช้ 2 ฟังก์ชันไลบรารี__CIsinและ__CIcos.
Vlad

13

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


จากนั้นค่าอินพุตจะต้องแมปกับ [0,2 * pi] (หรือเล็กกว่าด้วยการตรวจสอบเพิ่มเติม) และการเรียก fmod นี้จะทำให้ประสิทธิภาพลดลง ในการใช้งาน (propably suboptimal) ของฉันฉันไม่สามารถเพิ่มประสิทธิภาพด้วยตารางการค้นหา คุณมีคำแนะนำที่นี่หรือไม่?
Danvil

11
ตารางที่มีการคำนวณล่วงหน้าเกือบจะช้ากว่าการเรียกเพียงอย่างเดียวsinเนื่องจากตารางที่มีการคำนวณล่วงหน้าจะทิ้งแคช
Andreas Brinck

1
ขึ้นอยู่กับว่าโต๊ะใหญ่แค่ไหน ตาราง 256 รายการมักจะมีความแม่นยำเพียงพอและใช้เพียง 1Kb ... ถ้าคุณใช้มันมาก ๆ มันจะไม่ติดอยู่ในแคชโดยไม่ส่งผลเสียต่อประสิทธิภาพที่เหลือของแอพ?
คุณบอย

@Danvil: นี่คือตัวอย่างของไซน์ค้นหาตารางen.wikipedia.org/wiki/Lookup_table#Computing_sines อย่างไรก็ตามถือว่าคุณได้แมปอินพุตของคุณกับ [0; 2pi] แล้วด้วย
tanascius

@AndreasBrinck ฉันจะไม่ไปไกลขนาดนั้น ขึ้นอยู่กับ (TM) แคชสมัยใหม่มีขนาดใหญ่และตารางการค้นหามีขนาดเล็ก บ่อยครั้งหากคุณใช้ความระมัดระวังในการจัดวางหน่วยความจำตารางการค้นหาของคุณไม่จำเป็นต้องสร้างความแตกต่างใด ๆ กับการใช้แคชของการคำนวณที่เหลือของคุณ ความจริงที่ว่าตารางการค้นหาพอดีกับภายในแคชเป็นสาเหตุหนึ่งที่ทำให้เร็วมาก แม้ใน Java ซึ่งยากที่จะควบคุมเลย์เอาต์ mem ได้อย่างแม่นยำ แต่ฉันก็มีประสิทธิภาพที่ยอดเยี่ยมด้วยตารางการค้นหา
Jarrod Smith

13

เทคนิคที่คุณต้องการให้บรรลุนี้โดยใช้ตัวเลขที่ซับซ้อนและสูตรออยเลอร์ ดังนั้นบางอย่างเช่น (C ++)

complex<double> res = exp(complex<double>(0, x));
// or equivalent
complex<double> res = polar<double>(1, x);
double sin_x = res.imag();
double cos_x = res.real();

ควรให้ไซน์และโคไซน์ในขั้นตอนเดียว วิธีดำเนินการภายในเป็นคำถามเกี่ยวกับคอมไพเลอร์และไลบรารีที่ใช้ อาจ (และอาจ) ใช้เวลานานกว่าในการทำเช่นนี้ (เพียงเพราะสูตรของออยเลอร์ส่วนใหญ่จะใช้ในการคำนวณเชิงซ้อนexpโดยใช้sinและcos- ไม่ใช่วิธีอื่น) แต่อาจมีการเพิ่มประสิทธิภาพทางทฤษฎีได้บ้าง


แก้ไข

ส่วนหัวใน<complex>GNU C ++ 4.2 ใช้การคำนวณอย่างชัดเจนsinทั้งcosภายในpolarดังนั้นจึงดูไม่ดีเกินไปสำหรับการเพิ่มประสิทธิภาพที่นั่นเว้นแต่คอมไพเลอร์จะใช้เวทมนตร์ (ดู-ffast-mathและ-mfpmathสลับตามที่เขียนในคำตอบของ Chi )


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

12

คุณสามารถคำนวณอย่างใดอย่างหนึ่งแล้วใช้ข้อมูลประจำตัว:

cos (x) 2 = 1 - บาป (x) 2

แต่อย่างที่ @tanascius กล่าวตารางที่มีการคำนวณล่วงหน้าเป็นหนทางที่จะไป


8
และโปรดทราบว่าการใช้วิธีนี้เกี่ยวข้องกับการคำนวณกำลังและรากที่สองดังนั้นหากประสิทธิภาพมีความสำคัญตรวจสอบให้แน่ใจว่าสิ่งนี้เร็วกว่าการคำนวณฟังก์ชันตรีโกณโดยตรง
Tyler McHenry

4
sqrt()มักจะมีการเพิ่มประสิทธิภาพในด้านฮาร์ดแวร์ดังนั้นมันอาจจะดีมากจะได้เร็วขึ้นแล้วหรือsin() อำนาจอยู่เพียงแค่คูณด้วยตนเองจึงไม่ใช้cos() pow()มีเทคนิคบางอย่างเพื่อให้ได้รากที่สองที่แม่นยำพอสมควรอย่างรวดเร็วโดยไม่ต้องรองรับฮาร์ดแวร์ สุดท้ายนี้ให้แน่ใจว่าได้โพรไฟล์ก่อนดำเนินการใด ๆ
deft_code

12
โปรดทราบว่า√ (1 - cos ^ 2 x) มีความแม่นยำน้อยกว่าการคำนวณ sin x โดยตรงโดยเฉพาะเมื่อ x ~ 0
kennytm

1
สำหรับ x ขนาดเล็กชุด Taylor สำหรับ y = sqrt (1-x * x) นั้นดีมาก คุณจะได้รับความแม่นยำที่ดีด้วย 3 เทอมแรกและต้องใช้การคูณเพียงไม่กี่ครั้งและการกะครั้งเดียว ฉันใช้มันในรหัสจุดตายตัว
phkahler

1
@phkahler: ชุด Taylor ของคุณใช้ไม่ได้เพราะเมื่อ x ~ 0, cos x ~ 1
kennytm

10

หากคุณใช้ไลบรารี GNU C คุณสามารถทำได้:

#define _GNU_SOURCE
#include <math.h>

และคุณจะได้รับการประกาศของsincos(), sincosf()และsincosl()ฟังก์ชั่นที่คำนวณค่าทั้งสองร่วมกัน - สันนิษฐานในวิธีที่เร็วที่สุดสำหรับสถาปัตยกรรมเป้าหมายของคุณ


8

มีสิ่งที่น่าสนใจมากในหน้าฟอรัมนี้ซึ่งมุ่งเน้นไปที่การค้นหาการประมาณที่ดีที่รวดเร็ว: http://www.devmaster.net/forums/showthread.php?t=5784

คำเตือน: ไม่ได้ใช้สิ่งนี้ด้วยตัวเอง

อัปเดต 22 ก.พ. 2561: Wayback Machine เป็นวิธีเดียวในการเยี่ยมชมหน้าเดิมตอนนี้: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- ไซน์ - โคไซน์


ฉันลองตัวนี้เช่นกันและให้ประสิทธิภาพที่ดีทีเดียว แต่บาปและ cos จะคำนวณแยกกัน
Danvil

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

ไม่ตรง (แม้ว่าคำถามจะถามตรงนี้ก็ตาม) ฉันต้องการค่า sin และ cos ของค่า x และไม่มีทางรู้ได้ว่าที่อื่นฉันคำนวณ x + pi / 2 โดยบังเอิญหรือไม่ ...
Danvil

ฉันใช้มันในเกมเพื่อวาดวงกลมของอนุภาค เนื่องจากเป็นเพียงเอฟเฟกต์ภาพผลลัพธ์จึงใกล้เคียงเพียงพอและความสมบูรณ์แบบก็น่าประทับใจจริงๆ
Maxim Kamalov

ฉันไม่ประทับใจ การประมาณค่า Chebyshevมักจะให้ความแม่นยำสูงสุดสำหรับการแสดงที่กำหนด
Jason S

7

ห้องสมุดคณิตศาสตร์ C จำนวนมากตามที่ Caf ระบุมี sincos () อยู่แล้ว ข้อยกเว้นที่น่าสังเกตคือ MSVC

  • ซันมี sincos () มาตั้งแต่ปี 2530 เป็นอย่างน้อย (ยี่สิบสามปีฉันมีหน้าคนทำสำเนา)
  • HPUX 11 มีในปี 1997 (แต่ไม่มีใน HPUX 10.20)
  • เพิ่มไปยัง glibc ในเวอร์ชัน 2.1 (กุมภาพันธ์ 2542)
  • กลายเป็น gcc 3.4 (2004) ในตัว, __builtin_sincos ()

และเกี่ยวกับการค้นหา Eric S. Raymond ในArt of Unix Programming (2004) (บทที่ 12) กล่าวอย่างชัดเจนว่านี่เป็นความคิดที่ไม่ดี (ในช่วงเวลาปัจจุบัน):

"อีกตัวอย่างหนึ่งคือการคำนวณตารางขนาดเล็กไว้ล่วงหน้าตัวอย่างเช่นตาราง sin (x) ตามองศาสำหรับการปรับการหมุนให้เหมาะสมในเอ็นจิ้นกราฟิก 3 มิติจะใช้เวลา 365 × 4 ไบต์ในเครื่องสมัยใหม่ก่อนที่โปรเซสเซอร์จะเร็วกว่าหน่วยความจำเพียงพอที่จะต้องการแคช นี่คือการเพิ่มประสิทธิภาพความเร็วที่เห็นได้ชัดปัจจุบันการคำนวณซ้ำในแต่ละครั้งอาจเร็วกว่าที่จะจ่ายเงินสำหรับเปอร์เซ็นต์ของแคชเพิ่มเติมที่พลาดจากตาราง

"แต่ในอนาคตสิ่งนี้อาจเกิดขึ้นอีกครั้งเมื่อแคชขยายใหญ่ขึ้นโดยทั่วไปแล้วการเพิ่มประสิทธิภาพจำนวนมากจะเกิดขึ้นเพียงชั่วคราวและสามารถเปลี่ยนเป็นการมองโลกในแง่ร้ายได้ง่ายเมื่ออัตราส่วนต้นทุนเปลี่ยนไปวิธีเดียวที่จะทราบได้คือการวัดและดู" (จากArt of Unix Programming )

แต่การตัดสินจากการอภิปรายข้างต้นทุกคนไม่เห็นด้วย


10
"365 x 4 ไบต์" คุณต้องคิดเป็นปีอธิกสุรทินดังนั้นจึงควรเป็น 365.25 x 4 ไบต์ หรือบางทีเขาอาจหมายถึงการใช้จำนวนองศาในวงกลมแทนจำนวนวันในหนึ่งปีโลก
Ponkadoodle

@Wallacoloo: การสังเกตที่ดี ฉันคิดถึงมัน. แต่ข้อผิดพลาดอยู่ในที่เดิม
Joseph Quinsey

ฮ่า ๆ. นอกจากนี้เขายังละเลยความจริงที่ว่าในเกมคอมพิวเตอร์หลายเกมในพื้นที่นั้นคุณจะต้องมีมุม จำกัด จำนวนหนึ่งเท่านั้น ไม่มีแคชพลาดถ้าคุณรู้มุมที่เป็นไปได้ ฉันจะใช้ตารางในกรณีนี้และลองใช้fsincos(คำสั่ง CPU!) สำหรับคนอื่น ๆ มักจะเร็วพอ ๆ กับการแก้ไข sin และ cos จากตารางขนาดใหญ่
Erich Schubert

5

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

แต่ฉันจะมองหาการใช้งาน SinCos ที่รวดเร็วที่คุณพบในไลบรารีเช่น ACML ของ AMD และ MKL ของ Intel


3

หากคุณยินดีที่จะใช้ผลิตภัณฑ์เชิงพาณิชย์และกำลังคำนวณจำนวนการคำนวณ sin / cos ในเวลาเดียวกัน (เพื่อให้คุณสามารถใช้ฟังก์ชัน vectored ได้) คุณควรตรวจสอบMath Kernel Library ของ Intel

มีฟังก์ชั่น sincos

ตามเอกสารนั้นมันเฉลี่ย 13.08 นาฬิกา / องค์ประกอบบน core 2 duo ในโหมดความแม่นยำสูงซึ่งฉันคิดว่าจะเร็วกว่า fsincos ด้วยซ้ำ


1
ในทำนองเดียวกันบน OSX สามารถใช้vvsincosหรือvvsincosfจาก Accelerate.framework ฉันเชื่อว่า AMD มีฟังก์ชั่นที่คล้ายกันในไลบรารีเวกเตอร์เช่นกัน
Stephen Canon

3

บทความนี้แสดงวิธีสร้างอัลกอริทึมพาราโบลาที่สร้างทั้งไซน์และโคไซน์:

DSP Trick: การประมาณพาราโบลาพร้อมกันของ Sin และ Cos

http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos


1
อืม ... ฉันต้องยิงระหว่างนี้กับการประมาณของ Chebyshevซึ่งฉันคิดว่าจะชนะ
Jason S

2

เมื่อประสิทธิภาพเป็นสิ่งสำคัญสำหรับสิ่งนี้จึงไม่ใช่เรื่องแปลกที่จะแนะนำตารางการค้นหา


2

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

numerator = x
denominator = 1
sine = x
cosine = 1
op = -1
fact = 1

while (not enough precision) {
    fact++
    denominator *= fact
    numerator *= x

    cosine += op * numerator / denominator

    fact++
    denominator *= fact
    numerator *= x

    sine += op * numerator / denominator

    op *= -1
}

ซึ่งหมายความว่าคุณทำสิ่งนี้: เริ่มต้นที่ x และ 1 สำหรับ sin และ cosine ทำตามรูปแบบ - ลบ x ^ 2/2! จากโคไซน์ลบ x ^ 3/3! จากไซน์เพิ่ม x ^ 4/4! ในโคไซน์เพิ่ม x ^ 5/5! เพื่อไซน์ ...

ฉันไม่รู้ว่านี่จะเป็นนักแสดงหรือเปล่า หากคุณต้องการความแม่นยำน้อยกว่าที่สร้างขึ้นใน sin () และ cos () ให้คุณอาจเป็นตัวเลือก


ที่จริงแล้ว i-the sine extension factor คือ x / i คูณตัวประกอบการขยายโคไซน์ของ i แต่ฉันคงสงสัยว่าการใช้ซีรี่ส์ Taylor นั้นเร็วจริงๆ ...
Danvil

1
Chebyshev ดีกว่า Taylor มากสำหรับการประมาณค่าฟังก์ชันพหุนาม อย่าใช้การประมาณแบบเทย์เลอร์
Timmmm

มีจำนวนของ faux pas ที่เป็นตัวเลขอยู่ที่นี่ ทั้งตัวเศษและตัวส่วนมีขนาดใหญ่อย่างรวดเร็วและนำไปสู่ข้อผิดพลาดทศนิยม ไม่ต้องพูดถึงคุณจะตัดสินใจได้อย่างไรว่าอะไรคือ "ความแม่นยำไม่เพียงพอ" และจะคำนวณอย่างไร การประมาณเทย์เลอร์เป็นสิ่งที่ดีในบริเวณใกล้เคียงจุดเดียว จากจุดนั้นพวกเขากลายเป็นไม่ถูกต้องอย่างรวดเร็วและต้องการคำศัพท์จำนวนมากซึ่งเป็นเหตุผลว่าทำไมคำแนะนำของ Timmmm เกี่ยวกับการประมาณแบบ Chebyshev (ซึ่งสร้างการประมาณที่ดีในช่วงเวลาที่กำหนด) เป็นสิ่งที่ดี
Jason S

2

มีทางออกที่ดีในไลบรารี CEPHES ซึ่งค่อนข้างเร็วและคุณสามารถเพิ่ม / ลบความแม่นยำได้ค่อนข้างยืดหยุ่นสำหรับเวลา CPU ที่มากขึ้น / น้อยลง

จำไว้ว่า cos (x) และ sin (x) เป็นส่วนจริงและจินตภาพของ exp (ix) เราจึงต้องการคำนวณ exp (ix) เพื่อให้ได้ทั้งสองอย่าง เราคำนวณค่า exp (iy) ล่วงหน้าสำหรับค่าที่ไม่ต่อเนื่องของ y ระหว่าง 0 ถึง 2pi เราเลื่อน x ไปที่ช่วงเวลา [0, 2pi) จากนั้นเราเลือก y ที่ใกล้เคียงที่สุดกับ x และเขียน
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy))

เราได้รับ exp (iy) จากตารางการค้นหา และตั้งแต่ | xy | มีขนาดเล็ก (มากที่สุดครึ่งหนึ่งของระยะห่างระหว่างค่า y) อนุกรมเทย์เลอร์จะมาบรรจบกันอย่างสวยงามในไม่กี่คำดังนั้นเราจึงใช้มันสำหรับ exp (i (xy)) จากนั้นเราก็ต้องมีการคูณที่ซับซ้อนเพื่อให้ได้ exp (ix)

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


2

คุณอาจต้องการดูhttp://gruntthepeon.free.fr/ssemath/ซึ่งนำเสนอการใช้งาน SSE vectorized ที่ได้รับแรงบันดาลใจจากห้องสมุด CEPHES มีความแม่นยำที่ดี (ค่าเบี่ยงเบนสูงสุดจาก sin / cos ตามลำดับ 5e-8) และความเร็ว (มีประสิทธิภาพดีกว่า fsincos เล็กน้อยในการโทรครั้งเดียวและผู้ชนะที่ชัดเจนในหลายค่า)




0

คุณเคยคิดที่จะประกาศตารางการค้นหาสำหรับทั้งสองฟังก์ชันหรือไม่? คุณยังคงต้อง "คำนวณ" sin (x) และ cos (x) แต่จะตัดสินใจได้เร็วกว่าถ้าคุณไม่ต้องการความแม่นยำในระดับสูง


0

คอมไพเลอร์ MSVC อาจใช้ฟังก์ชัน SSE2 (ภายใน)

 ___libm_sse2_sincos_ (for x86)
 __libm_sse2_sincos_  (for x64)

ในบิลด์ที่ปรับให้เหมาะสมหากระบุแฟล็กคอมไพเลอร์ที่เหมาะสม (อย่างน้อยที่สุด / O2 / arch: SSE2 / fp: fast) ชื่อของฟังก์ชันเหล่านี้ดูเหมือนจะบอกเป็นนัยว่าไม่ได้คำนวณ sin และ cos ที่แยกจากกัน แต่ทั้งสอง "ในขั้นตอนเดียว"

ตัวอย่างเช่น:

void sincos(double const x, double & s, double & c)
{
  s = std::sin(x);
  c = std::cos(x);
}

การประกอบ (สำหรับ x86) ด้วย / fp: fast:

movsd   xmm0, QWORD PTR _x$[esp-4]
call    ___libm_sse2_sincos_
mov     eax, DWORD PTR _s$[esp-4]
movsd   QWORD PTR [eax], xmm0
mov     eax, DWORD PTR _c$[esp-4]
shufpd  xmm0, xmm0, 1
movsd   QWORD PTR [eax], xmm0
ret     0

แอสเซมบลี (สำหรับ x86) ที่ไม่มี / fp: fast แต่มี / fp: precision แทน (ซึ่งเป็นค่าเริ่มต้น) เรียก sin และ cos แยกกัน:

movsd   xmm0, QWORD PTR _x$[esp-4]
call    __libm_sse2_sin_precise
mov     eax, DWORD PTR _s$[esp-4]
movsd   QWORD PTR [eax], xmm0
movsd   xmm0, QWORD PTR _x$[esp-4]
call    __libm_sse2_cos_precise
mov     eax, DWORD PTR _c$[esp-4]
movsd   QWORD PTR [eax], xmm0
ret     0

ดังนั้น / fp: จำเป็นสำหรับการเพิ่มประสิทธิภาพ sincos

แต่โปรดทราบว่า

___libm_sse2_sincos_

อาจจะไม่แม่นยำเท่า

__libm_sse2_sin_precise
__libm_sse2_cos_precise

เนื่องจากไม่มีคำว่า "ที่แน่นอน" ต่อท้ายชื่อ

ในระบบที่เก่ากว่า "เล็กน้อย" ของฉัน (Intel Core 2 Duo E6750) ที่มีคอมไพเลอร์ MSVC 2019 ล่าสุดและการเพิ่มประสิทธิภาพที่เหมาะสมเกณฑ์มาตรฐานของฉันแสดงให้เห็นว่าการโทรแบบ sincos นั้นเร็วกว่าการเรียก sin และ cos ที่แยกกันประมาณ 2.4 เท่า

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