Skip to content

Commit be16a28

Browse files
committed
- Additional debug / verification of flight perf stuff
- Replaced usages of Vector3.Zero() by our own method that *actually* set the vector components to zero, fixes a few inconsistencies/bugs in the flight perf implementations (including a few that exist in stock) - Added ULP based FP equality comparer methods
1 parent 3571856 commit be16a28

File tree

2 files changed

+249
-36
lines changed

2 files changed

+249
-36
lines changed

KSPCommunityFixes/Library/Numerics.cs

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Linq;
33
using System.Runtime.CompilerServices;
4+
using System.Runtime.InteropServices;
45
using UnityEngine;
56
using 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

Comments
 (0)