Skip to content

Commit

Permalink
Add Equals(T other, T maxError), obsolete Equals(T other) for Double …
Browse files Browse the repository at this point in the history
…quantities

Equality comparison is not safe with System.Double as internal representation.
Decimal quantities (Power, Information) still allow equality.
Add test on new Equals() method.
  • Loading branch information
angularsen committed Nov 9, 2017
1 parent e0eb3f0 commit 471d2fc
Show file tree
Hide file tree
Showing 58 changed files with 901 additions and 9 deletions.
2 changes: 1 addition & 1 deletion UnitsNet.Tests/GeneratedCode/LapseRateTestsBase.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// </auto-generated>
//------------------------------------------------------------------------------

// Copyright (c) 2007 Andreas Gullberg Larsen (angularsen@gmail.com).
// Copyright (c) 2007 Andreas Gullberg Larsen (andreas.larsen84@gmail.com).
// https://github.com/angularsen/UnitsNet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
31 changes: 31 additions & 0 deletions UnitsNet.Tests/GeneratedQuantityCodeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Xunit;

namespace UnitsNet.Tests
{
/// <summary>
/// Tests that cover generated code in quantity structs, such as <see cref="Length" /> and <see cref="Mass" />.
/// The idea is to move tests that cover uniformly generated code here, because repeating the exact same test N times
/// over don't make it safer.
/// </summary>
public class GeneratedQuantityCodeTests
{
/// <summary>
/// Types with <see cref="double" /> as internal representation. This is the majority of units, such as
/// <see cref="Length" />.
/// </summary>
public class QuantitiesWithDouble
{
[Fact]
public void LengthEquals_GivenMaxError_ReturnsTrueIfWithinError()
{
Length smallError = Length.FromMeters(1e-5);
Assert.True(Length.FromMeters(1).Equals(Length.FromMeters(1), Length.Zero), "Integer values have zero difference.");
Assert.True(Length.FromMeters(1).Equals(Length.FromMeters(1), smallError), "Using a max difference value should not change that fact.");

Assert.False(Length.FromMeters(1 + 0.39).Equals(Length.FromMeters(1.39), Length.Zero),
"Example of floating precision arithmetic that produces slightly different results.");
Assert.True(Length.FromMeters(1 + 0.39).Equals(Length.FromMeters(1.39), smallError), "But the difference is very small");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// </auto-generated>
//------------------------------------------------------------------------------

// Copyright (c) 2007 Andreas Gullberg Larsen (angularsen@gmail.com).
// Copyright (c) 2007 Andreas Gullberg Larsen (andreas.larsen84@gmail.com).
// https://github.com/angularsen/UnitsNet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/Acceleration.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1060,19 +1060,22 @@ int CompareTo(Acceleration other)
return left._meterPerSecondSquared > right._meterPerSecondSquared;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(Acceleration left, Acceleration right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._meterPerSecondSquared == right._meterPerSecondSquared;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(Acceleration left, Acceleration right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._meterPerSecondSquared != right._meterPerSecondSquared;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -1083,6 +1086,19 @@ public override bool Equals(object obj)
return _meterPerSecondSquared.Equals(((Acceleration) obj)._meterPerSecondSquared);
}

/// <summary>
/// Compare equality to another Acceleration by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(Acceleration other, Acceleration maxError)
{
return Math.Abs(_meterPerSecondSquared - other._meterPerSecondSquared) <= maxError._meterPerSecondSquared;
}

public override int GetHashCode()
{
return _meterPerSecondSquared.GetHashCode();
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/AmountOfSubstance.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1830,19 +1830,22 @@ int CompareTo(AmountOfSubstance other)
return left._moles > right._moles;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(AmountOfSubstance left, AmountOfSubstance right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._moles == right._moles;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(AmountOfSubstance left, AmountOfSubstance right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._moles != right._moles;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -1853,6 +1856,19 @@ public override bool Equals(object obj)
return _moles.Equals(((AmountOfSubstance) obj)._moles);
}

/// <summary>
/// Compare equality to another AmountOfSubstance by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(AmountOfSubstance other, AmountOfSubstance maxError)
{
return Math.Abs(_moles - other._moles) <= maxError._moles;
}

public override int GetHashCode()
{
return _moles.GetHashCode();
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/AmplitudeRatio.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -738,19 +738,22 @@ int CompareTo(AmplitudeRatio other)
return left._decibelVolts > right._decibelVolts;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(AmplitudeRatio left, AmplitudeRatio right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._decibelVolts == right._decibelVolts;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(AmplitudeRatio left, AmplitudeRatio right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._decibelVolts != right._decibelVolts;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -761,6 +764,19 @@ public override bool Equals(object obj)
return _decibelVolts.Equals(((AmplitudeRatio) obj)._decibelVolts);
}

/// <summary>
/// Compare equality to another AmplitudeRatio by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(AmplitudeRatio other, AmplitudeRatio maxError)
{
return Math.Abs(_decibelVolts - other._decibelVolts) <= maxError._decibelVolts;
}

public override int GetHashCode()
{
return _decibelVolts.GetHashCode();
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/Angle.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1720,19 +1720,22 @@ int CompareTo(Angle other)
return left._degrees > right._degrees;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(Angle left, Angle right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._degrees == right._degrees;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(Angle left, Angle right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._degrees != right._degrees;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -1743,6 +1746,19 @@ public override bool Equals(object obj)
return _degrees.Equals(((Angle) obj)._degrees);
}

/// <summary>
/// Compare equality to another Angle by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(Angle other, Angle maxError)
{
return Math.Abs(_degrees - other._degrees) <= maxError._degrees;
}

public override int GetHashCode()
{
return _degrees.GetHashCode();
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/ApparentPower.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -620,19 +620,22 @@ int CompareTo(ApparentPower other)
return left._voltamperes > right._voltamperes;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(ApparentPower left, ApparentPower right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._voltamperes == right._voltamperes;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(ApparentPower left, ApparentPower right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._voltamperes != right._voltamperes;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -643,6 +646,19 @@ public override bool Equals(object obj)
return _voltamperes.Equals(((ApparentPower) obj)._voltamperes);
}

/// <summary>
/// Compare equality to another ApparentPower by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(ApparentPower other, ApparentPower maxError)
{
return Math.Abs(_voltamperes - other._voltamperes) <= maxError._voltamperes;
}

public override int GetHashCode()
{
return _voltamperes.GetHashCode();
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/Area.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1610,19 +1610,22 @@ int CompareTo(Area other)
return left._squareMeters > right._squareMeters;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(Area left, Area right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._squareMeters == right._squareMeters;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(Area left, Area right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._squareMeters != right._squareMeters;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -1633,6 +1636,19 @@ public override bool Equals(object obj)
return _squareMeters.Equals(((Area) obj)._squareMeters);
}

/// <summary>
/// Compare equality to another Area by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(Area other, Area maxError)
{
return Math.Abs(_squareMeters - other._squareMeters) <= maxError._squareMeters;
}

public override int GetHashCode()
{
return _squareMeters.GetHashCode();
Expand Down
16 changes: 16 additions & 0 deletions UnitsNet/GeneratedCode/Quantities/AreaMomentOfInertia.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -950,19 +950,22 @@ int CompareTo(AreaMomentOfInertia other)
return left._metersToTheFourth > right._metersToTheFourth;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator ==(AreaMomentOfInertia left, AreaMomentOfInertia right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._metersToTheFourth == right._metersToTheFourth;
}

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public static bool operator !=(AreaMomentOfInertia left, AreaMomentOfInertia right)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return left._metersToTheFourth != right._metersToTheFourth;
}
#endif

[Obsolete("It is not safe to compare equality due to using System.Double as the internal representation. It is very easy to get slightly different values due to floating point operations. Instead use Equals(other, maxError) to provide the max allowed error.")]
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Expand All @@ -973,6 +976,19 @@ public override bool Equals(object obj)
return _metersToTheFourth.Equals(((AreaMomentOfInertia) obj)._metersToTheFourth);
}

/// <summary>
/// Compare equality to another AreaMomentOfInertia by specifying a max allowed difference.
/// Note that it is advised against specifying zero difference, due to the nature
/// of floating point operations and using System.Double internally.
/// </summary>
/// <param name="other">Other quantity to compare to.</param>
/// <param name="maxError">Max error allowed.</param>
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
public bool Equals(AreaMomentOfInertia other, AreaMomentOfInertia maxError)
{
return Math.Abs(_metersToTheFourth - other._metersToTheFourth) <= maxError._metersToTheFourth;
}

public override int GetHashCode()
{
return _metersToTheFourth.GetHashCode();
Expand Down
Loading

0 comments on commit 471d2fc

Please sign in to comment.