1
1
using System ;
2
2
using System . Linq ;
3
3
using System . Runtime . CompilerServices ;
4
+ using System . Runtime . InteropServices ;
4
5
using UnityEngine ;
5
6
using Random = System . Random ;
6
7
@@ -9,13 +10,21 @@ namespace KSPCommunityFixes.Library
9
10
internal static class Numerics
10
11
{
11
12
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
12
- public static void Zero ( ref this Vector3 v )
13
+ public static void MutateZero ( ref this Vector3 v )
13
14
{
14
15
v . x = 0f ;
15
16
v . y = 0f ;
16
17
v . z = 0f ;
17
18
}
18
19
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
+
19
28
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
20
29
public static void CopyFrom ( ref this Vector3d v , Vector3 other )
21
30
{
@@ -412,6 +421,158 @@ public static unsafe double FastPow(double value, double exponent)
412
421
long tmp2 = ( long ) ( b_faction * ( tmp - 4606921280493453312L ) ) + 4606921280493453312L ;
413
422
return r * * ( double * ) & tmp2 ;
414
423
}
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
+ }
415
576
}
416
577
417
578
/// <summary>
0 commit comments