@@ -99,10 +99,10 @@ public static Fix64 Ceiling(Fix64 value) {
9999 public static Fix64 Round ( Fix64 value ) {
100100 var decimalPart = value . m_rawValue & 0x00000000FFFFFFFF ;
101101 var integralPart = Floor ( value ) ;
102- if ( decimalPart < 0x0000000080000000 ) {
102+ if ( decimalPart < 0x80000000 ) {
103103 return integralPart ;
104104 }
105- if ( decimalPart > 0x0000000080000000 ) {
105+ if ( decimalPart > 0x80000000 ) {
106106 return integralPart + One ;
107107 }
108108 // if number is halfway between two values, round to the nearest even number
@@ -426,35 +426,59 @@ public static Fix64 Sqrt(Fix64 x) {
426426 return new Fix64 ( ( long ) result ) ;
427427 }
428428
429-
429+ /// <summary>
430+ /// Returns the Sine of x.
431+ /// This function is accurate to around 3 * Fix64.Precision for small enough values of x.
432+ /// It may lose accuracy as the value of x grows.
433+ /// Performance: about 25% slower than Math.Sin() in x64, and 200% slower in x86.
434+ /// </summary>
430435 public static Fix64 Sin ( Fix64 x ) {
431-
432436 var xl = x . m_rawValue ;
433- // FIXME 30-50% of the execution time is these modulos...
434- // We could eliminate 2 of them, as well as the flipVertical/horizontal logic,
435- // by making the table go from 0 to 2*pi instead of pi/2;
436- // that would make it 4 times larger though.
437- var clamped2Pi = xl % PI_TIMES_2 + ( xl < 0 ? PI_TIMES_2 : 0 ) ;
437+
438+ // Clamp value to 0 - 2*PI using modulo; this is very slow but there's no better way AFAIK
439+ var clamped2Pi = xl % PI_TIMES_2 ;
440+ if ( xl < 0 ) {
441+ clamped2Pi += PI_TIMES_2 ;
442+ }
443+
444+ // The LUT contains values for 0 - PiOver2; every other value must be obtained by
445+ // vertical or horizontal mirroring
438446 var flipVertical = clamped2Pi >= PI ;
439- var flipHorizontal = clamped2Pi % PI >= PI_OVER_2 ;
440- var clamped = new Fix64 ( clamped2Pi % PI_OVER_2 ) ;
447+ // obtain (angle % PI) from (angle % 2PI) - much faster than doing another modulo
448+ var clampedPi = clamped2Pi ;
449+ while ( clampedPi >= PI ) {
450+ clampedPi -= PI ;
451+ }
452+ var flipHorizontal = clampedPi >= PI_OVER_2 ;
453+ // obtain (angle % PI_OVER_2) from (angle % PI) - much faster than doing another modulo
454+ var clampedPiOver2 = clampedPi ;
455+ if ( clampedPiOver2 >= PI_OVER_2 ) {
456+ clampedPiOver2 -= PI_OVER_2 ;
457+ }
458+ var clamped = new Fix64 ( clampedPiOver2 ) ;
441459
460+ // Find the two closest values in the LUT and perform linear interpolation
461+ // This is unfortunately very expensive on x86; a "FastSin" could skip most of this
462+ // at the expense of accuracy
442463 var rawIndex = FastMul ( clamped , SinInterval ) ;
443464 var roundedIndex = Round ( rawIndex ) ;
444465 var indexError = FastSub ( rawIndex , roundedIndex ) ;
445466
446467 var nearestValue = new Fix64 ( SinLut [ flipHorizontal ?
447468 SinLut . Length - 1 - ( int ) roundedIndex :
448469 ( int ) roundedIndex ] ) ;
449- var nextNearestValue = new Fix64 ( SinLut [ flipHorizontal ?
470+ var secondNearestValue = new Fix64 ( SinLut [ flipHorizontal ?
450471 SinLut . Length - 1 - ( int ) roundedIndex - Sign ( indexError ) :
451472 ( int ) roundedIndex + Sign ( indexError ) ] ) ;
452- var delta = FastMul ( indexError , FastAbs ( FastSub ( nearestValue , nextNearestValue ) ) ) ;
453- delta = flipHorizontal ? - delta : delta ;
454- var interpolatedValue = FastAdd ( nearestValue , delta ) ;
455473
474+ var delta = FastMul ( indexError , FastAbs ( FastSub ( nearestValue , secondNearestValue ) ) ) . m_rawValue ;
475+ var interpolatedValue = nearestValue . m_rawValue + ( flipHorizontal ? - delta : delta ) ;
456476 var finalValue = flipVertical ? - interpolatedValue : interpolatedValue ;
457- return finalValue ;
477+ return new Fix64 ( finalValue ) ;
478+ //var nearestValue = SinLut[flipHorizontal ?
479+ // SinLut.Length - 1 - (int)rawIndex :
480+ // (int)rawIndex];
481+ //return new Fix64(flipVertical ? -nearestValue : nearestValue);
458482 }
459483
460484
0 commit comments