2

I'd like to know what the C# equivalent of the following VB6 code is that will generate the exact same results:

Public Function Moo(o1 As Currency, o2 As Double) As Currency
    Moo = Round((o1 * o2) / 100, 2)
End Function

Before you answer with Math.Round(o1 * (decimal)o2 / 100, 2), consider the following test case:

o1 = 1450 o2 = 1.15

The VB6 function returns 16.67 and that C# code returns 16.68.

Next option: (decimal)Math.Round((double)o1 * o2 / 100, 2)

That generates the same result but then there's this test case:

o1 = 1570 o2 = 1.15

Now, the VB code generates 18.06 but the C# generates 18.05.

I understand the reasoning behind why this is happening and why one should never use double for calculations that require accuracy and that the VB6 code is doing things it shouldn't. Consider this an academic exercise. Essentially, is it possible to get the following test to pass:

[Theory]
[InlineData(1450, 1.15, 16.67)]
[InlineData(1570, 1.15, 18.06)]
public void MooTest(decimal o1, double o2, decimal expectedValue) {
    var value = {MAGICAL METHOD CODE HERE};
    Assert.Equal(expectedValue, value);
}
Kyle Baley
  • 580
  • 1
  • 5
  • 16
  • 4
    Before you make sure C# matches VB6 you should probably make sure [C# matches C#](https://stackoverflow.com/q/6683059/11683). – GSerg Mar 20 '23 at 16:16
  • Which argument is `basis` and which is `rate`? Your parameter names don't match the signature. – Jonathan Wood Mar 20 '23 at 16:42
  • 1
    After 25 years working with systems in a bank, I would suggest stop using currency type for currency, even more, stop using decimal or float, take everything in long (or byte[] if the long max is reached) as an integer of cents – Leandro Bardelli Mar 20 '23 at 16:54
  • 2
    What is the result of the calculation before calling `Round` in VB6 – Charlieface Mar 20 '23 at 17:14
  • @LeandroBardelli This isn't a case of which one is correct. The requirement under discussion was "it has to match the legacy code regardless of which one is correct" – Kyle Baley Mar 20 '23 at 18:56
  • I'm not in favor of respect the legacy since it's pretty clear a vb error. In the begin, vb is a language made with C :S – Leandro Bardelli Mar 20 '23 at 19:51
  • in one case you have to round up with a 5, in the case you dont. yes you can get the tests to pass if you use example specific ifs, but thats stupid just to get the unit test to pass. – Molbac Mar 21 '23 at 07:50
  • VB6/VBA's `Round()` uses what's called Banker's Rounding, which is achieved by setting the [`MidpointRounding.ToEven`](https://learn.microsoft.com/en-us/dotnet/api/system.math.round?view=net-7.0) flag with .NET's Round(). – Hel O'Ween Mar 21 '23 at 11:11
  • @HelO'Ween Math.Round defaults to MidpointRounding.ToEven if you leave it off. I've verified that VB6 and C# both round consistently. Here, I believe it's an issue with how VB6 handles doubles – Kyle Baley Mar 21 '23 at 12:49
  • @LeandroBardelli Agreed but in many cases "correct" often takes a backseat to "don't rock the boat". All we can do is make a recommendation on how much stuff will cost and leave the final decision to those who pay the bill. – Kyle Baley Mar 21 '23 at 12:52
  • 1
    [Is this the magical method you're looking for?](https://stackoverflow.com/a/39741404/690141) – Jimmy Smith Mar 21 '23 at 23:35
  • @JimmySmith That looks promising though I'll have to find a Linux equivalent for it – Kyle Baley Mar 22 '23 at 00:18
  • Ah Linux, I'd go with libc, [getround](https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-fegetround) and `fesetround (0)` I believe is what you'd temporarily set – Jimmy Smith Mar 22 '23 at 01:40

0 Answers0