2

I am re-writing a COBOL program in C# that does calculations dealing with money. From some of the first calculations however, some of the decimal rounding is off (doesn't match what the COBOL program calculates).

Decimal.Round(19.87 * 2.57, 2) returns 51.07 (51.0659) in C#,

COMPUTE varA = 19.87 * 2.57 returns 51.06 in COBOL

Here COBOL rounds the number down, but in other cases it rounds up where C# rounds down. I'm at a loss.

This is one of the first calculations I have to do, so once the numbers get larger they are more and more different from the COBOL output. (I have a report I am testing numbers against.)

I have tried Math.Round, and Floor and Ceiling of both classes to get the output I should see to no avail.

UPDATE: I found what my problem was. Many of the fields used in the calculations in the COBOL program had 9 decimal places, but the result was stored in a 2 decimal place field. I was doing the calculations after truncating, instead of before and then truncating. Thanks everyone for your help! Every answer helped!

JB06
  • 1,881
  • 14
  • 28
  • 1
    `Here COBOL rounds the number down, but in other cases it rounds up where C# rounds down. I'm at a loss` - this (apparent) inconsistency smells of banker's rounding to me. See http://www.xbeat.net/vbspeed/i_BankersRounding.htm – Konrad Morawski Apr 01 '14 at 13:15
  • statistical/Gaussian rounding vs "standard" rounding? – SQLMason Apr 01 '14 at 13:15
  • Have a look at [compute rounded in cobol](http://stackoverflow.com/questions/7572553/compute-rounded-in-cobol) – huMpty duMpty Apr 01 '14 at 13:16
  • See http://msdn.microsoft.com/en-us/library/system.midpointrounding.aspx (there is an overload of `Math.Round` that takes an argument of this type). Try both values... – Konrad Morawski Apr 01 '14 at 13:17
  • Bankers rounding doesn't apply here. This isn't a midpoint. I would suggest this is a rounding vs truncation issue. Taking a look at [this question](http://stackoverflow.com/questions/7572553/compute-rounded-in-cobol) it appears to me that to get the value to round, you need to specify that in the command `ROUNDED` perhaps? I'm not, nor ever will be, a COBOL developer lol – Adam Houldsworth Apr 01 '14 at 13:18
  • @KonradMorawski I have tried those MidPointRounding overloads, neither solve the problem, it seems they both round the number up anyway. – JB06 Apr 01 '14 at 13:21
  • @Josh This is because that value isn't a midpoint value relative to the number of decimal places you want, so that setting won't even be enacted. To see an example of midpoint rounding, change your value to `51.065` and try the two options again. Banker's Rounding will yield 51.06, and midpoint will yield 51.07. – Adam Houldsworth Apr 01 '14 at 13:25
  • 1
    Glad you're getting somewhere. Be aware (as you can see from the linked questions in my answer) that there is no need for vast numbers of decimal places in a COMPUTE. If the decimal places do not (potentially) contain significant digits it means the original coder does not know how to use COMPUTE effectively, and no-one has dared touch it since. Possibly suggests you'll find discrepancies elsewhere. If you need to ask again, make sure you include all the field definitions. Good luck. – Bill Woodger Apr 01 '14 at 20:01
  • 1
    In Cobol you are most likely using Fixed length decimal numbers, while in C# are you using standard Floating / Double. The 2 will never be exactly the same. – Bruce Martin Apr 01 '14 at 20:03

3 Answers3

5

The COBOL COMPUTE that you show is simply rounding down (aka truncating). The intermediate result will be held with two decimal places and the excess value that you may expect if you used a calculator will simply disappear.

If you do not replicate that in behaviour in your new program, you will not match the figures.

It is of course possible that the COBOL program should be rounding, but you'll have to get someone to look into that.

COMPUTE varA ROUNDED = 19.87 * 2.57

Would get you 51.07, since the intermediate result will be to three decimal places, then if that third decimal place is 5-9, the second decimal place will be increased by one and only then would the result be truncated.

Be aware that the difference between COMPUTE with and without ROUNDED is important in determining your correct values. Be also aware that there are two more COBOL verbs, DIVIDE and MULTIPLY, which would naturally behave differently depending on data with and without ROUNDED. Be aware that ADD and SUBTRACT where different numbers of decimal places are involved would also...

Be also aware that the more complex a COMPUTE is, the more likely it has been coded without thought to what actual intermediate values are used. See these two, for instance: COBOL COMPUTE calculation, AS/400: Using COMPUTE function, inconsistent results with different field definition

Be also aware that the current COBOL standard is the 1985 standard, with 1989 corrections and extensions for Intrinsic Functions. The new standard is not complete, and although new elements in that are implemented in some compilers, there are not many which have implemented the definitions of different types of rounding. Without ROUNDED anyway, you will get "rounded down".

Community
  • 1
  • 1
Bill Woodger
  • 12,968
  • 4
  • 38
  • 47
2

First, I believe you need to determine the version of COBOL you are migrating from. Second, be careful of any time there is a COMPUTE ROUNDED operation as it will differ from the plain COMPUTE you have shown us.

Some helpful information on Cobol Compute here that may help you understand.

To solve your example: If you take your C# computation to three decimal places then truncate, you'll mirror the COBOL example you provided without having it rounded up. See this question for assistance on truncating numbers.

Please note that this assumes ALL your COBOL will act this way. You must verify this.

Community
  • 1
  • 1
bland
  • 1,968
  • 1
  • 15
  • 22
  • COMPUTE is COBOL Standard. Any COBOL which does not have a standard COMPUTE is not going to be worth the paper it is written on. So, for this, not necessary to know which COBOL it is, although otherwise, of course, it is highly useful advice. – Bill Woodger Apr 01 '14 at 13:31
  • That won't mirror the COBOL (which is simply to have two decimals for the intermediate), but it will give the same result without some silly multiply and divide that other answers are suggesting :-) – Bill Woodger Apr 01 '14 at 13:32
1

COBOL

varA seems to be defined to store 2 decimal positions. As no ROUNDED option has been specified, the multiplication result will be truncated to fit into this. This is the default behavior.

C#

You can get the same result by using Math.Truncate(x * 100m) / 100m;

Reda
  • 2,289
  • 17
  • 19