#1336 Decimal precision

katox Sun 28 Nov 2010

Thinking of sys::Decimal as the default Fantom decimal type it feels weird not to have a precision control over it.

How should one do financial calculations with no control of rounding for instance?

I'm not saying we should jump on clumsy MathContext API of Java's BigDecimal a few additional functions would suffice.

brian Mon 29 Nov 2010

Any concrete suggestions?

We could just add a scale and toScale method?

jodastephen Mon 29 Nov 2010

I've wondered if there is an option to use a 64 bit integer type and use pure functions to manipulate it.

Decimal a := 1.234
Decimal b := 2.345
Decimal c := a + b
// which becomes
long c = DecimalFns.plus(a, b);  // Java

This would give decimals similar performance as other primitives, although its a lot of work to do. You'd also need a full BigDecimal for larger numbers. But it does work well for the main use case of simply getting accurate numbers. There is an IEEE standard for 64 bit decimals too which I remember seeing a Java library for.

brian Mon 29 Nov 2010

I've wondered if there is an option to use a 64 bit integer type and use pure functions to manipulate it

This is how decimal works in C#, and is how I would prefer Fantom Decimal work too. Most people just want to use decimal for money, and the overhead of BigDecimal is probably excessive (that could always be exposed in a math pod). However, implementing decimal with a 64-bit long is a pretty big project that I wasn't ready to tackle. Do you know what library you saw that in?

jodastephen Mon 29 Nov 2010

Agreed that it would be a big project (and the need to retain a BigDecimal).

I think this is the library. Although I've no idea how up to date or accurate it is.

katox Mon 29 Nov 2010

Yes, my thoughts were to add

  • Int scale() - returning current scale
  • Decimal toScale(Int newScale, RoundingMode mode := RoundingMode.halfEven) - expanding/shrinking (if possible) the scale
  • Decimal round(RoundingMode mode := RoundingMode.halfEven) - rounds to the nearest integer value using given rounding type, mostly convenience function

where Rounding mode would be simply the same as in Java.

helium Tue 30 Nov 2010

.Net's decimal is 128 bits wide.

brian Tue 30 Nov 2010

I'd really hate to have to add a RoundingMode enum into sys - is that something people really use? Or is it just over-engineering?

katox Tue 30 Nov 2010

I'm not very fond of making it more complicated but unfortunately this isn't the type of problem "one glove fits all".

Frequent usage:

  • half up - taught in school, very popular, biased (0.5 always get rounded up)
  • half to even - statistical rounding, free of overall bias, the default for financial computing ) and floating point operations (IEEE 754)
  • up / down - like floor and ceiling but rounded in direction from zero to +inf/-inf, default for arithmetic computing

Infrequent usage:

  • floor - cut the decimal part, used in engineering
  • ceiling - round nearest bigger integer (+1 if there is decimal part), used in engineering
  • stochastic - random up/down rounding, using in statistics
  • unnecessary - forcing an exception if rounding is needed

Almost never used

  • half down - complement to half up
  • half to odd - similar to half to even but preserves scale

Note: rounding modes were added on request in Java 5+. Dotnet System.Decimal supports half to even (the default) and arithmetic rounding (via MidporintRounding enum)

dmoebius Wed 1 Dec 2010

+1 for a real number tower.

I've seen that in the Factor programming language, and it works.

brian Wed 1 Dec 2010

A real numeric tower is hard to efficiently implement without direct JVM support.

Regarding enhancements to Decimal, to tell the truth I am still a bit on the fence about adding all this stuff to 1.0. It seems to draw into revolving all Fantom's semantics around Java's BigDecimal and is that what we really want?

katox Wed 1 Dec 2010

@brian, I see you point. We take the less general way and implement it more like sys::Float.

New operations

for sys::Decimal

  • Int scale()
  • Decimal toScale(Int newScale, RoundingMode mode := RoundingMode.halfUp)
  • Decimal round(RoundingMode mode := RoundingMode.halfUp)
  • Decimal floor()
  • Decimal ceil()

and RoundingMode enum of two values only

  • halfUp
  • halfToEven

Calls using toScale with different rounding modes would have to be emulated by shifting decimal point left scale places, calling floor/ceil, shifting right.

Change note: I'd leave the rounding default on halfUp because that is what JS and Java returns (unlike dotnet).

Implementation

scale

toScale and round

HALF EVEN (default financial and float rounding)
3.5  -> 4
2.5  -> 2
1.5  -> 2
1.1  -> 1
-1.1 -> -1
-1.5 -> -2
-2.5 -> -2
-3.5 -> -4

round()

  • dotnet: Decimal.Round(decimal, 0), Decimal.Round(decimal, 0, MidpointRounding.ToEven)
  • js: implementation

toScale()

  • dotnet: Decimal.Round(decimal, newScale), Decimal.Round(decimal, newScale, MidpointRounding.ToEven)
  • js: needs impl

-

HALF UP (up if fraction >= 0.5, down otherwise, arithmetic rounding)
3.5  -> 4
2.5  -> 3
1.5  -> 2
1.1  -> 1
-1.1 -> -1
-1.5 -> -2
-2.5 -> -3
-3.5 -> -4

round()

  • dotnet: Decimal.Round(decimal, 0, MidpointRounding.AwayFromZero)
  • js: Math.round(decimal), decimal.toFixed(0)

toScale()

  • dotnet: Decimal.Round(decimal, newScale, MidpointRounding.AwayFromZero)
  • js: decimal.toFixed(newScale)

ceil

CEILING (towards +inf)
3.5  -> 4
2.5  -> 3
1.5  -> 2
1.1  -> 2
-1.1 -> -1
-1.5 -> -1
-2.5 -> -2
-3.5 -> -3
  • dotnet: Decimal.Ceiling(decimal)
  • js: Math.ceil(decimal)

floor

FLOOR (towards -inf)
3.5  -> 3
2.5  -> 2
1.5  -> 1
1.1  -> 1
-1.1 -> -2
-1.5 -> -2
-2.5 -> -3
-3.5 -> -4
  • dotnet: Decimal.Floor(decimal)
  • js: Math.floor(decimal)

Unimplemented

These would need manual reimplementation in apps.

UP (away from zero)
3.5  -> 4
2.5  -> 3
1.5  -> 2
1.1  -> 2
-1.1 -> -1
-1.5 -> -2
-2.5 -> -3
-3.5 -> -4
DOWN (towards zero, cut fraction)
3.5  -> 3
2.5  -> 2
1.5  -> 1
1.1  -> 1
-1.1 -> -1
-1.5 -> -1
-2.5 -> -2
-3.5 -> -3
HALF_DOWN (half up complement) // no reasonable usage
3.5  -> 3
2.5  -> 2
1.5  -> 2
1.1  -> 1
-1.1 -> -1
-1.6 -> -2
-2.5 -> -2
-3.5 -> -3

Login or Signup to reply.