11using System ;
22using System . Linq ;
33using System . Runtime . CompilerServices ;
4+ using System . Runtime . InteropServices ;
45using UnityEngine ;
56using Random = System . Random ;
67
@@ -9,13 +10,21 @@ namespace KSPCommunityFixes.Library
910 internal static class Numerics
1011 {
1112 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
12- public static void Zero ( ref this Vector3 v )
13+ public static void MutateZero ( ref this Vector3 v )
1314 {
1415 v . x = 0f ;
1516 v . y = 0f ;
1617 v . z = 0f ;
1718 }
1819
20+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
21+ public static void MutateZero ( ref this Vector3d v )
22+ {
23+ v . x = 0.0 ;
24+ v . y = 0.0 ;
25+ v . z = 0.0 ;
26+ }
27+
1928 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
2029 public static void CopyFrom ( ref this Vector3d v , Vector3 other )
2130 {
@@ -412,6 +421,158 @@ public static unsafe double FastPow(double value, double exponent)
412421 long tmp2 = ( long ) ( b_faction * ( tmp - 4606921280493453312L ) ) + 4606921280493453312L ;
413422 return r * * ( double * ) & tmp2 ;
414423 }
424+
425+ // ULP equality helpers from https://github.com/nunit/nunit/blob/a39e8297b4e9ad279679430021af7a83f3091d59/src/NUnitFramework/framework/Constraints/FloatingPointNumerics.cs
426+
427+ /// <summary>Union of a floating point variable and an integer</summary>
428+ [ StructLayout ( LayoutKind . Explicit ) ]
429+ private struct FloatIntUnion
430+ {
431+ /// <summary>The union's value as a floating point variable</summary>
432+ [ FieldOffset ( 0 ) ]
433+ public float Float ;
434+
435+ /// <summary>The union's value as an integer</summary>
436+ [ FieldOffset ( 0 ) ]
437+ public int Int ;
438+
439+ /// <summary>The union's value as an unsigned integer</summary>
440+ [ FieldOffset ( 0 ) ]
441+ public uint UInt ;
442+ }
443+
444+ /// <summary>Union of a double precision floating point variable and a long</summary>
445+ [ StructLayout ( LayoutKind . Explicit ) ]
446+ private struct DoubleLongUnion
447+ {
448+ /// <summary>The union's value as a double precision floating point variable</summary>
449+ [ FieldOffset ( 0 ) ]
450+ public double Double ;
451+
452+ /// <summary>The union's value as a long</summary>
453+ [ FieldOffset ( 0 ) ]
454+ public long Long ;
455+
456+ /// <summary>The union's value as an unsigned long</summary>
457+ [ FieldOffset ( 0 ) ]
458+ public ulong ULong ;
459+ }
460+
461+ /// <summary>Compares two floating point values for equality</summary>
462+ /// <param name="left">First floating point value to be compared</param>
463+ /// <param name="right">Second floating point value t be compared</param>
464+ /// <param name="maxUlps">
465+ /// Maximum number of representable floating point values that are allowed to
466+ /// be between the left and the right floating point values
467+ /// </param>
468+ /// <returns>True if both numbers are equal or close to being equal</returns>
469+ /// <remarks>
470+ /// <para>
471+ /// Floating point values can only represent a finite subset of natural numbers.
472+ /// For example, the values 2.00000000 and 2.00000024 can be stored in a float,
473+ /// but nothing between them.
474+ /// </para>
475+ /// <para>
476+ /// This comparison will count how many possible floating point values are between
477+ /// the left and the right number. If the number of possible values between both
478+ /// numbers is less than or equal to maxUlps, then the numbers are considered as
479+ /// being equal.
480+ /// </para>
481+ /// <para>
482+ /// Implementation partially follows the code outlined here:
483+ /// http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/
484+ /// </para>
485+ /// </remarks>
486+ public static bool AlmostEqual ( float left , float right , int maxUlps )
487+ {
488+ FloatIntUnion leftUnion = new FloatIntUnion ( ) ;
489+ FloatIntUnion rightUnion = new FloatIntUnion ( ) ;
490+
491+ leftUnion . Float = left ;
492+ rightUnion . Float = right ;
493+
494+ uint leftSignMask = ( leftUnion . UInt >> 31 ) ;
495+ uint rightSignMask = ( rightUnion . UInt >> 31 ) ;
496+
497+ uint leftTemp = ( ( 0x80000000 - leftUnion . UInt ) & leftSignMask ) ;
498+ leftUnion . UInt = leftTemp | ( leftUnion . UInt & ~ leftSignMask ) ;
499+
500+ uint rightTemp = ( ( 0x80000000 - rightUnion . UInt ) & rightSignMask ) ;
501+ rightUnion . UInt = rightTemp | ( rightUnion . UInt & ~ rightSignMask ) ;
502+
503+ if ( leftSignMask != rightSignMask ) // Overflow possible, check each against zero
504+ {
505+ // This check is specifically used to trap the case of 0 == -0
506+ // In IEEE floating point maths, -0 is converted to Float.MinValue, which cannot be used with
507+ // Math.Abs(...) below due to overflow issues. This should only match the 0 == -0 condition.
508+ if ( left == right )
509+ return true ;
510+
511+ if ( Math . Abs ( leftUnion . Int ) > maxUlps || Math . Abs ( rightUnion . Int ) > maxUlps )
512+ return false ;
513+ }
514+
515+ // Either they have the same sign or both are very close to zero
516+ return Math . Abs ( leftUnion . Int - rightUnion . Int ) <= maxUlps ;
517+ }
518+
519+ /// <summary>Compares two double precision floating point values for equality</summary>
520+ /// <param name="left">First double precision floating point value to be compared</param>
521+ /// <param name="right">Second double precision floating point value t be compared</param>
522+ /// <param name="maxUlps">
523+ /// Maximum number of representable double precision floating point values that are
524+ /// allowed to be between the left and the right double precision floating point values
525+ /// </param>
526+ /// <returns>True if both numbers are equal or close to being equal</returns>
527+ /// <remarks>
528+ /// <para>
529+ /// Double precision floating point values can only represent a limited series of
530+ /// natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004
531+ /// can be stored in a double, but nothing between them.
532+ /// </para>
533+ /// <para>
534+ /// This comparison will count how many possible double precision floating point
535+ /// values are between the left and the right number. If the number of possible
536+ /// values between both numbers is less than or equal to maxUlps, then the numbers
537+ /// are considered as being equal.
538+ /// </para>
539+ /// <para>
540+ /// Implementation partially follows the code outlined here:
541+ /// http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/
542+ /// </para>
543+ /// </remarks>
544+ public static bool AlmostEqual ( double left , double right , long maxUlps )
545+ {
546+ DoubleLongUnion leftUnion = new DoubleLongUnion ( ) ;
547+ DoubleLongUnion rightUnion = new DoubleLongUnion ( ) ;
548+
549+ leftUnion . Double = left ;
550+ rightUnion . Double = right ;
551+
552+ ulong leftSignMask = ( leftUnion . ULong >> 63 ) ;
553+ ulong rightSignMask = ( rightUnion . ULong >> 63 ) ;
554+
555+ ulong leftTemp = ( ( 0x8000000000000000 - leftUnion . ULong ) & leftSignMask ) ;
556+ leftUnion . ULong = leftTemp | ( leftUnion . ULong & ~ leftSignMask ) ;
557+
558+ ulong rightTemp = ( ( 0x8000000000000000 - rightUnion . ULong ) & rightSignMask ) ;
559+ rightUnion . ULong = rightTemp | ( rightUnion . ULong & ~ rightSignMask ) ;
560+
561+ if ( leftSignMask != rightSignMask ) // Overflow possible, check each against zero
562+ {
563+ // This check is specifically used to trap the case of 0 == -0
564+ // In IEEE floating point maths, -0 is converted to Double.MinValue, which cannot be used with
565+ // Math.Abs(...) below due to overflow issues. This should only match the 0 == -0 condition.
566+ if ( left == right )
567+ return true ;
568+
569+ if ( Math . Abs ( leftUnion . Long ) > maxUlps || Math . Abs ( rightUnion . Long ) > maxUlps )
570+ return false ;
571+ }
572+
573+ // Either they have the same sign or both are very close to zero
574+ return Math . Abs ( leftUnion . Long - rightUnion . Long ) <= maxUlps ;
575+ }
415576 }
416577
417578 /// <summary>
0 commit comments