tools4j / decimal4j

Java library for fast fixed-point arithmetic based on longs with support for up to 18 decimal places.
decimal4j.org
MIT License
156 stars 17 forks source link

adding stream map / reduce handling to decimal4j #24

Closed terzerm closed 1 year ago

terzerm commented 1 year ago

hi Marco,

Love your github work. Are you thinking about adding stream map / reduce handling to decimal4j? I am currently refactoring our project to use your Decimal2f but lacking support for map/reduce. Example:

return positions.stream().filter(position -> position.open) .map(position -> position.openPnL).reduce(0.0, Double::sum);

[...] BR, Greg

terzerm commented 1 year ago

Hi Greg,

Thanks for your interest in decimal4j and for your question.

I believe map/reduce is already possible today:

final double[] pnl = {1, 2, 3, 3.5, 3.2, 4.1};

//1) with immutable decimals
final Decimal2f totalPnl = DoubleStream.of(pnl)
        .mapToObj(Decimal2f::valueOf)
        .reduce(Decimal2f.ZERO, Decimal2f::add);
assertEquals("16.80", totalPnl.toString());
assertEquals(Decimal2f.valueOf(16.8), totalPnl);

//2) with mutable decimals
final MutableDecimal2f mutableTotalPnl = DoubleStream.of(pnl)
        .mapToObj(MutableDecimal2f::new)
        .reduce(MutableDecimal2f.zero(), MutableDecimal2f::add);
assertEquals("16.80", mutableTotalPnl.toString());
assertEquals(new MutableDecimal2f(16.8), mutableTotalPnl);

//3) with zero-GC API (NOTE: streams and map/reduce still causes object allocation)
final DecimalArithmetic arithRoundHalfEven = Scale2f.INSTANCE.getRoundingHalfEvenArithmetic();
final DecimalArithmetic arithNoRounding = Scale2f.INSTANCE.getRoundingUnnecessaryArithmetic();
//NOTE: accessing instance method refs causes allocation, hence cache them usually in a constant
final DoubleToLongFunction fromDouble2f = arithRoundHalfEven::fromDouble;
final LongBinaryOperator add2f = arithNoRounding::add;
final long totalPnlUnscaled = DoubleStream.of(pnl)
        .mapToLong(fromDouble2f)
        .reduce(0, add2f);
assertEquals("16.80", arithNoRounding.toString(totalPnlUnscaled));
assertEquals(arithRoundHalfEven.fromDouble(16.8), totalPnlUnscaled);

Above code has also been added as issue24() test method to FaqTest.java

Hope this helps.

Best Regards, Marco