Skip to content

Commit 471d2fc

Browse files
committed
Add Equals(T other, T maxError), obsolete Equals(T other) for Double 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.
1 parent e0eb3f0 commit 471d2fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+901
-9
lines changed

UnitsNet.Tests/GeneratedCode/LapseRateTestsBase.g.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// </auto-generated>
1616
//------------------------------------------------------------------------------
1717

18-
// Copyright (c) 2007 Andreas Gullberg Larsen (angularsen@gmail.com).
18+
// Copyright (c) 2007 Andreas Gullberg Larsen (andreas.larsen84@gmail.com).
1919
// https://github.com/angularsen/UnitsNet
2020
//
2121
// Permission is hereby granted, free of charge, to any person obtaining a copy
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Xunit;
2+
3+
namespace UnitsNet.Tests
4+
{
5+
/// <summary>
6+
/// Tests that cover generated code in quantity structs, such as <see cref="Length" /> and <see cref="Mass" />.
7+
/// The idea is to move tests that cover uniformly generated code here, because repeating the exact same test N times
8+
/// over don't make it safer.
9+
/// </summary>
10+
public class GeneratedQuantityCodeTests
11+
{
12+
/// <summary>
13+
/// Types with <see cref="double" /> as internal representation. This is the majority of units, such as
14+
/// <see cref="Length" />.
15+
/// </summary>
16+
public class QuantitiesWithDouble
17+
{
18+
[Fact]
19+
public void LengthEquals_GivenMaxError_ReturnsTrueIfWithinError()
20+
{
21+
Length smallError = Length.FromMeters(1e-5);
22+
Assert.True(Length.FromMeters(1).Equals(Length.FromMeters(1), Length.Zero), "Integer values have zero difference.");
23+
Assert.True(Length.FromMeters(1).Equals(Length.FromMeters(1), smallError), "Using a max difference value should not change that fact.");
24+
25+
Assert.False(Length.FromMeters(1 + 0.39).Equals(Length.FromMeters(1.39), Length.Zero),
26+
"Example of floating precision arithmetic that produces slightly different results.");
27+
Assert.True(Length.FromMeters(1 + 0.39).Equals(Length.FromMeters(1.39), smallError), "But the difference is very small");
28+
}
29+
}
30+
}
31+
}

UnitsNet/GeneratedCode/Extensions/Number/NumberToLapseRateExtensions.g.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// </auto-generated>
1616
//------------------------------------------------------------------------------
1717

18-
// Copyright (c) 2007 Andreas Gullberg Larsen (angularsen@gmail.com).
18+
// Copyright (c) 2007 Andreas Gullberg Larsen (andreas.larsen84@gmail.com).
1919
// https://github.com/angularsen/UnitsNet
2020
//
2121
// Permission is hereby granted, free of charge, to any person obtaining a copy

UnitsNet/GeneratedCode/Quantities/Acceleration.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,19 +1060,22 @@ int CompareTo(Acceleration other)
10601060
return left._meterPerSecondSquared > right._meterPerSecondSquared;
10611061
}
10621062

1063+
[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.")]
10631064
public static bool operator ==(Acceleration left, Acceleration right)
10641065
{
10651066
// ReSharper disable once CompareOfFloatsByEqualityOperator
10661067
return left._meterPerSecondSquared == right._meterPerSecondSquared;
10671068
}
10681069

1070+
[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.")]
10691071
public static bool operator !=(Acceleration left, Acceleration right)
10701072
{
10711073
// ReSharper disable once CompareOfFloatsByEqualityOperator
10721074
return left._meterPerSecondSquared != right._meterPerSecondSquared;
10731075
}
10741076
#endif
10751077

1078+
[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.")]
10761079
public override bool Equals(object obj)
10771080
{
10781081
if (obj == null || GetType() != obj.GetType())
@@ -1083,6 +1086,19 @@ public override bool Equals(object obj)
10831086
return _meterPerSecondSquared.Equals(((Acceleration) obj)._meterPerSecondSquared);
10841087
}
10851088

1089+
/// <summary>
1090+
/// Compare equality to another Acceleration by specifying a max allowed difference.
1091+
/// Note that it is advised against specifying zero difference, due to the nature
1092+
/// of floating point operations and using System.Double internally.
1093+
/// </summary>
1094+
/// <param name="other">Other quantity to compare to.</param>
1095+
/// <param name="maxError">Max error allowed.</param>
1096+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
1097+
public bool Equals(Acceleration other, Acceleration maxError)
1098+
{
1099+
return Math.Abs(_meterPerSecondSquared - other._meterPerSecondSquared) <= maxError._meterPerSecondSquared;
1100+
}
1101+
10861102
public override int GetHashCode()
10871103
{
10881104
return _meterPerSecondSquared.GetHashCode();

UnitsNet/GeneratedCode/Quantities/AmountOfSubstance.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,19 +1830,22 @@ int CompareTo(AmountOfSubstance other)
18301830
return left._moles > right._moles;
18311831
}
18321832

1833+
[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.")]
18331834
public static bool operator ==(AmountOfSubstance left, AmountOfSubstance right)
18341835
{
18351836
// ReSharper disable once CompareOfFloatsByEqualityOperator
18361837
return left._moles == right._moles;
18371838
}
18381839

1840+
[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.")]
18391841
public static bool operator !=(AmountOfSubstance left, AmountOfSubstance right)
18401842
{
18411843
// ReSharper disable once CompareOfFloatsByEqualityOperator
18421844
return left._moles != right._moles;
18431845
}
18441846
#endif
18451847

1848+
[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.")]
18461849
public override bool Equals(object obj)
18471850
{
18481851
if (obj == null || GetType() != obj.GetType())
@@ -1853,6 +1856,19 @@ public override bool Equals(object obj)
18531856
return _moles.Equals(((AmountOfSubstance) obj)._moles);
18541857
}
18551858

1859+
/// <summary>
1860+
/// Compare equality to another AmountOfSubstance by specifying a max allowed difference.
1861+
/// Note that it is advised against specifying zero difference, due to the nature
1862+
/// of floating point operations and using System.Double internally.
1863+
/// </summary>
1864+
/// <param name="other">Other quantity to compare to.</param>
1865+
/// <param name="maxError">Max error allowed.</param>
1866+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
1867+
public bool Equals(AmountOfSubstance other, AmountOfSubstance maxError)
1868+
{
1869+
return Math.Abs(_moles - other._moles) <= maxError._moles;
1870+
}
1871+
18561872
public override int GetHashCode()
18571873
{
18581874
return _moles.GetHashCode();

UnitsNet/GeneratedCode/Quantities/AmplitudeRatio.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,19 +738,22 @@ int CompareTo(AmplitudeRatio other)
738738
return left._decibelVolts > right._decibelVolts;
739739
}
740740

741+
[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.")]
741742
public static bool operator ==(AmplitudeRatio left, AmplitudeRatio right)
742743
{
743744
// ReSharper disable once CompareOfFloatsByEqualityOperator
744745
return left._decibelVolts == right._decibelVolts;
745746
}
746747

748+
[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.")]
747749
public static bool operator !=(AmplitudeRatio left, AmplitudeRatio right)
748750
{
749751
// ReSharper disable once CompareOfFloatsByEqualityOperator
750752
return left._decibelVolts != right._decibelVolts;
751753
}
752754
#endif
753755

756+
[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.")]
754757
public override bool Equals(object obj)
755758
{
756759
if (obj == null || GetType() != obj.GetType())
@@ -761,6 +764,19 @@ public override bool Equals(object obj)
761764
return _decibelVolts.Equals(((AmplitudeRatio) obj)._decibelVolts);
762765
}
763766

767+
/// <summary>
768+
/// Compare equality to another AmplitudeRatio by specifying a max allowed difference.
769+
/// Note that it is advised against specifying zero difference, due to the nature
770+
/// of floating point operations and using System.Double internally.
771+
/// </summary>
772+
/// <param name="other">Other quantity to compare to.</param>
773+
/// <param name="maxError">Max error allowed.</param>
774+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
775+
public bool Equals(AmplitudeRatio other, AmplitudeRatio maxError)
776+
{
777+
return Math.Abs(_decibelVolts - other._decibelVolts) <= maxError._decibelVolts;
778+
}
779+
764780
public override int GetHashCode()
765781
{
766782
return _decibelVolts.GetHashCode();

UnitsNet/GeneratedCode/Quantities/Angle.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,19 +1720,22 @@ int CompareTo(Angle other)
17201720
return left._degrees > right._degrees;
17211721
}
17221722

1723+
[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.")]
17231724
public static bool operator ==(Angle left, Angle right)
17241725
{
17251726
// ReSharper disable once CompareOfFloatsByEqualityOperator
17261727
return left._degrees == right._degrees;
17271728
}
17281729

1730+
[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.")]
17291731
public static bool operator !=(Angle left, Angle right)
17301732
{
17311733
// ReSharper disable once CompareOfFloatsByEqualityOperator
17321734
return left._degrees != right._degrees;
17331735
}
17341736
#endif
17351737

1738+
[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.")]
17361739
public override bool Equals(object obj)
17371740
{
17381741
if (obj == null || GetType() != obj.GetType())
@@ -1743,6 +1746,19 @@ public override bool Equals(object obj)
17431746
return _degrees.Equals(((Angle) obj)._degrees);
17441747
}
17451748

1749+
/// <summary>
1750+
/// Compare equality to another Angle by specifying a max allowed difference.
1751+
/// Note that it is advised against specifying zero difference, due to the nature
1752+
/// of floating point operations and using System.Double internally.
1753+
/// </summary>
1754+
/// <param name="other">Other quantity to compare to.</param>
1755+
/// <param name="maxError">Max error allowed.</param>
1756+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
1757+
public bool Equals(Angle other, Angle maxError)
1758+
{
1759+
return Math.Abs(_degrees - other._degrees) <= maxError._degrees;
1760+
}
1761+
17461762
public override int GetHashCode()
17471763
{
17481764
return _degrees.GetHashCode();

UnitsNet/GeneratedCode/Quantities/ApparentPower.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,19 +620,22 @@ int CompareTo(ApparentPower other)
620620
return left._voltamperes > right._voltamperes;
621621
}
622622

623+
[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.")]
623624
public static bool operator ==(ApparentPower left, ApparentPower right)
624625
{
625626
// ReSharper disable once CompareOfFloatsByEqualityOperator
626627
return left._voltamperes == right._voltamperes;
627628
}
628629

630+
[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.")]
629631
public static bool operator !=(ApparentPower left, ApparentPower right)
630632
{
631633
// ReSharper disable once CompareOfFloatsByEqualityOperator
632634
return left._voltamperes != right._voltamperes;
633635
}
634636
#endif
635637

638+
[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.")]
636639
public override bool Equals(object obj)
637640
{
638641
if (obj == null || GetType() != obj.GetType())
@@ -643,6 +646,19 @@ public override bool Equals(object obj)
643646
return _voltamperes.Equals(((ApparentPower) obj)._voltamperes);
644647
}
645648

649+
/// <summary>
650+
/// Compare equality to another ApparentPower by specifying a max allowed difference.
651+
/// Note that it is advised against specifying zero difference, due to the nature
652+
/// of floating point operations and using System.Double internally.
653+
/// </summary>
654+
/// <param name="other">Other quantity to compare to.</param>
655+
/// <param name="maxError">Max error allowed.</param>
656+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
657+
public bool Equals(ApparentPower other, ApparentPower maxError)
658+
{
659+
return Math.Abs(_voltamperes - other._voltamperes) <= maxError._voltamperes;
660+
}
661+
646662
public override int GetHashCode()
647663
{
648664
return _voltamperes.GetHashCode();

UnitsNet/GeneratedCode/Quantities/Area.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,19 +1610,22 @@ int CompareTo(Area other)
16101610
return left._squareMeters > right._squareMeters;
16111611
}
16121612

1613+
[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.")]
16131614
public static bool operator ==(Area left, Area right)
16141615
{
16151616
// ReSharper disable once CompareOfFloatsByEqualityOperator
16161617
return left._squareMeters == right._squareMeters;
16171618
}
16181619

1620+
[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.")]
16191621
public static bool operator !=(Area left, Area right)
16201622
{
16211623
// ReSharper disable once CompareOfFloatsByEqualityOperator
16221624
return left._squareMeters != right._squareMeters;
16231625
}
16241626
#endif
16251627

1628+
[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.")]
16261629
public override bool Equals(object obj)
16271630
{
16281631
if (obj == null || GetType() != obj.GetType())
@@ -1633,6 +1636,19 @@ public override bool Equals(object obj)
16331636
return _squareMeters.Equals(((Area) obj)._squareMeters);
16341637
}
16351638

1639+
/// <summary>
1640+
/// Compare equality to another Area by specifying a max allowed difference.
1641+
/// Note that it is advised against specifying zero difference, due to the nature
1642+
/// of floating point operations and using System.Double internally.
1643+
/// </summary>
1644+
/// <param name="other">Other quantity to compare to.</param>
1645+
/// <param name="maxError">Max error allowed.</param>
1646+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
1647+
public bool Equals(Area other, Area maxError)
1648+
{
1649+
return Math.Abs(_squareMeters - other._squareMeters) <= maxError._squareMeters;
1650+
}
1651+
16361652
public override int GetHashCode()
16371653
{
16381654
return _squareMeters.GetHashCode();

UnitsNet/GeneratedCode/Quantities/AreaMomentOfInertia.g.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,19 +950,22 @@ int CompareTo(AreaMomentOfInertia other)
950950
return left._metersToTheFourth > right._metersToTheFourth;
951951
}
952952

953+
[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.")]
953954
public static bool operator ==(AreaMomentOfInertia left, AreaMomentOfInertia right)
954955
{
955956
// ReSharper disable once CompareOfFloatsByEqualityOperator
956957
return left._metersToTheFourth == right._metersToTheFourth;
957958
}
958959

960+
[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.")]
959961
public static bool operator !=(AreaMomentOfInertia left, AreaMomentOfInertia right)
960962
{
961963
// ReSharper disable once CompareOfFloatsByEqualityOperator
962964
return left._metersToTheFourth != right._metersToTheFourth;
963965
}
964966
#endif
965967

968+
[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.")]
966969
public override bool Equals(object obj)
967970
{
968971
if (obj == null || GetType() != obj.GetType())
@@ -973,6 +976,19 @@ public override bool Equals(object obj)
973976
return _metersToTheFourth.Equals(((AreaMomentOfInertia) obj)._metersToTheFourth);
974977
}
975978

979+
/// <summary>
980+
/// Compare equality to another AreaMomentOfInertia by specifying a max allowed difference.
981+
/// Note that it is advised against specifying zero difference, due to the nature
982+
/// of floating point operations and using System.Double internally.
983+
/// </summary>
984+
/// <param name="other">Other quantity to compare to.</param>
985+
/// <param name="maxError">Max error allowed.</param>
986+
/// <returns>True if the difference between the two values is not greater than the specified max.</returns>
987+
public bool Equals(AreaMomentOfInertia other, AreaMomentOfInertia maxError)
988+
{
989+
return Math.Abs(_metersToTheFourth - other._metersToTheFourth) <= maxError._metersToTheFourth;
990+
}
991+
976992
public override int GetHashCode()
977993
{
978994
return _metersToTheFourth.GetHashCode();

0 commit comments

Comments
 (0)