Advertisement
Kaelygon

Sine approximations

Jun 29th, 2023 (edited)
786
0
Never
2
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.47 KB | None | 0 0
  1. //g++ -g -Os -O3 ./testing.cpp -o ./testing && ./testing
  2. #include <stdlib.h>
  3. #include <cstdint>
  4. #include <type_traits>
  5. #include <chrono>
  6. #include <iostream>
  7. #include <math.h>
  8.  
  9. //select uint type that is double of T width
  10. template <typename T>
  11. struct twiceWide {
  12.     using type = typename std::conditional<(sizeof(T) == 1), uint16_t,
  13.                    typename std::conditional<(sizeof(T) == 2), uint32_t,
  14.                    typename std::conditional<(sizeof(T) == 4), uint64_t,
  15.                    typename std::conditional<(sizeof(T) == 8), __uint128_t,
  16.                                                 void>::type>::type>::type>::type;
  17. };
  18.  
  19. //https://www.desmos.com/calculator/cydsdmvy2t
  20. //Fast sine approximation. range 0 to UINT_MAX.
  21. template <typename T>
  22. T ukaelSine(T num) {
  23.     using T2 = typename twiceWide<T>::type; //required for squaring
  24.     bool secondHalf = (num >> ((sizeof(T)<<3) - 1));    //sizeof(T)*8-1. values that're more than 0.5*UINT_MAX
  25.     num <<= 1;  //get 2 periods of saw wave
  26.     T2 buf = (static_cast<T2>(num)<<1) - ((T)~0);   //store to twiceWide to prevent overflow
  27.     num = static_cast<T>((buf * buf) >> ((sizeof(T)<<3) + 1));  //square and rever back to T scale
  28.     num = secondHalf ? num : ~num;  //invert 2nd half
  29.     return num;
  30. }
  31.  
  32. #define SAMPLES 16777216 //ram is your limit
  33. #define REPEATS 16        //or you can split the testing
  34.  
  35.  
  36. #define TEST_VALUE ( (i*i)   )  //squared
  37. //#define TEST_VALUE ( rand() ) //random
  38. //#define TEST_VALUE ( i         )  //linear
  39. //#define TEST_VALUE ( 1         )
  40.  
  41. #define STORE_DOUBLE 0  // [1] can have up to 50% impact in favor of sin()
  42.  
  43. //sin() over ukaelSine() time //-O3 -Os
  44. //higher is better in favor of ukaelSine
  45. //lower  is better in favor of sin()
  46. //STORE_DOUBLE==0
  47.     //sin(i*i)      = 28 times slower       //best case for ukaelSine. Especially when storing uint8_t *value[], sin() is 42 times slower
  48.     //sin(rand())   = 11 times slower
  49.     //sin(i)        = 6 times slower
  50.     //sin(1)        = ~1.0 and ~3 times faster without -O3 -Os flags. Best case scenario for sin()
  51.  
  52. //STORE_DOUBLE==1
  53.     //sin(i*i)      = 20 times slower
  54.     //sin(rand())   = 10 times slower
  55.     //sin(i)        =  4 times slower
  56.     //sin(1)        = ~1.0                  //worst case for ukaelSine
  57.  
  58. int main(){
  59.  
  60.     //warmup, gets your cpu's legs running
  61.     uint64_t warmup = 12385377835987337323UL;
  62.     for(int i=0;i<SAMPLES*REPEATS/4;i++){
  63.         warmup=(uint64_t)(1+3573489789832437712ULL*( ( atan2((double)warmup,warmup*M_PI) ) )); //the least efficient rng
  64.     }
  65.     std::cout << warmup << "\n"; //print that it's not optimized out by -O3 and -Os
  66.  
  67.     double testTime[2];
  68. //  for(int k=1;k>-1;k--){ //reverse test, minimal impact
  69.     for(int k=0;k<2;k++){ //0=ukael sine   1=math.h sine
  70.  
  71.         std::chrono::steady_clock::time_point timest = std::chrono::steady_clock::now(); //first timer
  72.  
  73.             for(int j=0;j<REPEATS;j++){
  74.  
  75. #if STORE_DOUBLE==0
  76.                 typedef uint32_t test_t;
  77. #else
  78.                 typedef double test_t;
  79. #endif
  80.  
  81.                 test_t *value;
  82.                 value = (test_t*) malloc(SAMPLES*sizeof(test_t));
  83.  
  84.                 for(uint32_t i=1;i<SAMPLES;i+=1){ //iterate through SAMPLES
  85.                     value[i] = k ?
  86.                         sin      ( (double)( (double)TEST_VALUE*1.23456789) ) //k==1
  87.                     :
  88.                         ukaelSine( (uint32_t)( (double)TEST_VALUE*1.23456789) ) //k==0
  89.                     ;
  90.                 }
  91.                 if(j==REPEATS-1){std::cout << "last value " << value[SAMPLES-1] << "\n";}//print last value before free
  92.                 free(value);    //Making sure that no chaching happens
  93.             }
  94.  
  95.         k==0 ? std::cout << "ukael sine " : std::cout << "math.h sine ";
  96.         testTime[k]=(double)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-timest).count()/1000000.0;
  97.         std::cout << testTime[k] << "\n";
  98.     }
  99.     std::cout << "time ratio: sin() / ukaelSine() " << testTime[1]/testTime[0] << "\n";
  100.     return 0;
  101. }
  102.  
  103.  
  104.  
  105. /*
  106. //alternative Sine but poor precision as this doesn't require twiceWide
  107. template <typename T>
  108. T ukaelCSine(T num) {
  109.     bool secondHalf = (num >> ((sizeof(T)<<3) - 1));
  110.     bool evenQuarter = (num >> ((sizeof(T)<<3) - 2))==0 || (num >> ((sizeof(T)<<3) - 2))==2;
  111.     num = evenQuarter ? ~num : num; //invert even quarters
  112.     num <<= 2;  //2 period saw
  113.     num >>= sizeof(T)<<2;   //square root. Crunchy precision
  114.     num*=num;   //square
  115.     num >>= 1;  //divide by 2
  116.     num = secondHalf ? num : ~num;  //invert 2nd half
  117.     return num;
  118. }
  119.  
  120.  
  121. // print example, paste in desmos to view. Converted to range 0 to 1
  122. #include <stdio.h> //printf
  123. int main(){
  124.     uint32_t samples = ((uint32_t)~0);
  125.     uint32_t inc = ((uint32_t)~0)/255;
  126.  
  127.     for(uint32_t i=0;i<samples-inc-1;i+=inc){
  128.         printf("(%f,%f)",(double)i/samples,(double)(ukaelSine(i))/samples );
  129.         if(i<samples-2*inc){printf(",");}
  130.     }
  131.  
  132.     return 0;
  133. }
  134. */
Advertisement
Comments
Add Comment
Please, Sign In to add comment
Advertisement