Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is not hypothetical.

Yes, of course IEEE decimal supports setting the rounding mode. The authors of the spec aren't ignorant of what's needed for financial and tax computations.

Use fe_dec_setround from ISO/IEC TR 24732, "Extension for the programming language C to support decimal floating-point arithmetic".

The modes listed at https://www.ibm.com/docs/en/zos/2.5.0?topic=functions-fe-dec... are:

  FE_DEC_DOWNWARD
    rounds towards minus infinity
  FE_DEC_TONEAREST
    rounds to nearest
  FE_DEC_TOWARDZERO
    rounds toward zero
  FE_DEC_UPWARD
    rounds toward plus infinity
  FE_DEC_TONEARESTFROMZERO
    rounds to nearest, ties away from zero
  _FE_DEC_AWAYFROMZERO
    rounds away from zero
  _FE_DEC_TONEARESTTOWARDZERO
    rounds to nearest, ties toward zero
  _FE_DEC_PREPAREFORSHORTER
    rounds to prepare for shorter precision


The authors of the spec made some provisions, and very likely all of them are useful and correct, the issue is how the programmers will use them, and in some cases there isn't even a "correct" solution that everyone uses.

A classic example in invoicing is an item that is advertised for 60.00 (to the final user) VAT 10% included.

If you try making an invoice for that sum in a few programs you will find three or four way it is implemented.

Some will have 54.54+5.45=59.99, some will have 54.54+5.46=60.00, some will have 54.55+5.46=60.01 (and possibly a "discount" of 0.01), some will have 54.545+5.45=60.00, some will have 54.54545454+5.45=60,00.


Yes, it isn't possible for the software to know your local accounting laws and practices.

My point is it's probably better to use existing, well-tested provisions than to build your own from scaled integers, to get one of those three results.

As a bonus, you might get hardware support in the future.


Yes, I understand what you are saying, I was highlighting that those (if adopted) would only fix (maybe) part of the problem, they are just (better) tools.

At the end of the day what I want (and I presume any other customer wants) is a correct invoice with the correct net, tax and total, and this will only happen when (if) the programmer understands the base issues and uses the correct library/algorithm/whatever.


This list seems to be missing something since afaik, IEEE floating point also specifies round-to-even (bankers' rounding) for round to nearest ties. Unless 'FE_DEC_TONEAREST' is that, the documentation does not say.

https://en.wikipedia.org/wiki/IEEE_754#Roundings_to_nearest

EDIT: apparently IEEE does not specify a "round-to-odd" for ties despite this having been used for banking in the UK :/

https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_odd


For goods items, Danish customs require specifying 3 decimals for weights under 1kg, otherwise no decimals. Off the top of my head I don't recall exactly how they expect rounding to be done, I'd guess towards infinity.

Many duties are calculated based on net weight, and often the net weight per goods line is the result of a calculation, for example you're importing N items with a per-item weight of X. If you have a large number of goods items above 1kg but less than 10kg that has weight-based duties, the rounding mode can matter a lot.

None of the rounding modes mentioned captures this below/above 1kg split, so you have to do this in code anyway. Might as well do the rounding there too, to be sure some injected code doesn't mess up the expected rounding mode or similar[1].

[1]: https://irrlicht.sourceforge.io/forum/viewtopic.php?t=8773


Sure, you'll need to handle special cases yourself. But perhaps you don't have to handle all the cases yourself?

As I understand it, one of the new things in IEEE 754 is the idea of a "context", which stores this information. This can be global, but does not need to be. With Python's decimal module it is a thread-local variable.

If you are concerned about, say, mixing thread-local and async, you can also use context methods directly, like:

  >>> import decimal
  >>> x = decimal.Decimal("54.1234")
  >>> y = decimal.Decimal("987.340")
  >>> x+y
  Decimal('1041.4634')
  >>> decimal.getcontext()
  Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
  capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero,
  Overflow])
  >>>
  >>> c1 = decimal.Context(prec=4)
  >>> c1.add(x, y)
  Decimal('1041')
  >>> c2 = decimal.Context(prec=5)
  >>> c2.add(x, y)
  Decimal('1041.5')
  >>> c3 = decimal.Context(prec=5, rounding=decimal.ROUND_DOWN)
  >>> c3.add(x, y)
  Decimal('1041.4')
I don't know what the C or C++ API proposals are.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: