• Walky_one (unregistered) in reply to bjolling
    bjolling:
    Walky_one:
    Round(11.5) - Round(10.5) = 2 Round(12.0) - Round(11.0) = 1 Round(12.5) - Round(11.5) = 0

    So if you have two values that have a fix distance of exactly 1, due to inconsistent rounding you get anything between 0 and 2.

    Example: You work with graphics and draw a rectangle from 10.5 to 20.5. Rounded to pixel values you get a 10-pixel wide rectangle.

    The error is in your example not in the rounding. If you really care about preserving distance you would round as late as possible, right before showing it on screen:

    Round(11.5 - 10.5) = 1 Round(12.0 - 11.0) = 1 Round(12.5 - 11.5) = 1

    You miss the point.

    The actual code is kind of: DrawRect(Round(X1),Round(Y1),Round(X2),Round(Y2)).

    Use: X1 = 11.5, X2 = 20.5 (Rectangle has a width of 8 pixels) X1 = 12.5, X2 = 21.5 (Rectangle has a width of 10 pixels)

    There is no way I can "combine" X1 and X2. (Well I principally could take the middle point, round it, take the distance, divide by two. Round it. Then add/subract. But then we just exchange the problem with a different one.

  • gnasher729 (unregistered) in reply to Pero perić
    Pero perić:
    Also why is round-half-even standardized instead of round-half-odd?

    It is standardized for binary floating-point arithmetic because it has the effect that the last binary bit is zero more often, and because of that further operations more often have no rounding error at all. For example, if you calculate 3 * (x + y) and the "round ties to even" rule is applied in the addition, then the multiplication by 3 will have no rounding error at all. If "round ties to odd" were used, then the multiplication by 3 would give another rounding error.

    For rounding to integers, this rounding method is actually unusual.

  • instigator (unregistered) in reply to TheCPUWizard
    TheCPUWizard:
    Bankers rounding is rounding to the even number in case the decimal is exactly 0.5.

    Or more precisely exactly 1/2 of the last significant digit...the irony is that many of these values can NOT be exactly specified in a binary number and therefore bankers rounding should NEVER occur (everything will be above or below the 1/2 way point)...which makes the end results really interesting.

    The (admittedly horrible) string based method actually avoids this issue.

    I believe you are mistaken. binary 1.1 should be decimal 1.5 (2^0 + 2^-1) binary 1.01 should be decimal 1.25 (2^0 + 2^-2) and so on.

    It is very natural for binary to hit a 1/2.

  • Erik Gern AKA Erro' (unregistered) in reply to gnasher729
    gnasher729:
    Anon:
    I've never been taught rounding other than 'Add 0.5 and then truncate to the integer part.'

    That's clean and simple, and never leaves room for ambiguity.

    So you would round -12.4 to -11.

    -12.4 + 0.5 = -11.9. Truncated to integer part = -11.

    Man, you can't spoil a story like this! A measly one-liner, what are you thinking? I could've written a 2000 word story from that.
  • himitsu (unregistered) in reply to Eric

    Set8087CW or SetRoundMode

    http://docwiki.embarcadero.com/CodeExamples/XE2/en/SetRoundMode_%28Delphi%29

  • Walky_one (unregistered) in reply to instigator
    instigator:
    TheCPUWizard:
    Bankers rounding is rounding to the even number in case the decimal is exactly 0.5.

    Or more precisely exactly 1/2 of the last significant digit...the irony is that many of these values can NOT be exactly specified in a binary number and therefore bankers rounding should NEVER occur (everything will be above or below the 1/2 way point)...which makes the end results really interesting.

    The (admittedly horrible) string based method actually avoids this issue.

    I believe you are mistaken. binary 1.1 should be decimal 1.5 (2^0 + 2^-1) binary 1.01 should be decimal 1.25 (2^0 + 2^-2) and so on.

    It is very natural for binary to hit a 1/2.

    Only looking at the fractional part: The case of .5 is indeed very exact.

    But almost any other fraction ending on 5 is not: 0.05, 0.15, 0.35, 0.005, 0.015, 0.025, 0,105, ...

    Only the special cases of y*1/(2^x) are exact numbers. The more digits you use, the higher the ratio of inexact numbers.

    On the other hand: if it's not an exact number, it will be rounded correctly anyway.

  • Walky_one (unregistered)

    To be precise:

    The number of "exact" values ending with a 5 at a certain digit are: 2^(digits - 1) The number of "values" ending with a 5 at a certain digit are: 10^(digits - 1)

    As you can see, the values of inexact digits will grow pretty fast

  • (cs) in reply to Walky_one
    Walky_one:
    And just generally: Bankers rounding might be the way to go for money related math. However if you work in other areas, it's nearly always plain wrong:

    Round(11.5) - Round(10.5) = 2 Round(12.0) - Round(11.0) = 1 Round(12.5) - Round(11.5) = 0

    Your example is incomplete and therefore misleading. Try taking the rounded value of the sequence 0.0 thru 9.9 (by hundredths) and see what it sums to for each version of rounding. Or, for that matter, generate a few thousand random floats, round them each way, and see which result sums closer to the non-rounded values. Rounding to the nearest even (or odd) will always be better.

  • (cs) in reply to cellocgw
    cellocgw:
    Rounding to the nearest even (or odd) will always be better.

    But only 2/3 of the time, and the other time you end up rounding the number into a goat.

  • (cs) in reply to Marphy
    Marphy:
    BCD is superior. All the exactness of strings, half the space. Plus the CPU has built-in hardware to do arithmetic on them.

    So, we should go back to IBM1620's (a very interesting machine by the way!).

    Lacking that, packed decimal on the IBM 360 was intended for Cobol!

  • (cs)

    I've always thought that accounting was the perversion of mathematics.

  • Shan (unregistered)

    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

  • QJo (unregistered) in reply to Shan
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    +1 This.

  • (cs) in reply to Shan
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    Currency conversion. Sure, you may have one dollar and thirteen cents, but that's not quite exactly 115.81 yen.

    Also, if you have 5.5% interest on that $1.13, what happens to the 0.215 of a cent?

    Especially since it's purely electronic, math sucks for money.

  • anonymous (unregistered) in reply to chubertdev
    chubertdev:
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    Currency conversion. Sure, you may have one dollar and thirteen cents, but that's not quite exactly 115.81 yen.

    Also, if you have 5.5% interest on that $1.13, what happens to the 0.215 of a cent?

    Especially since it's purely electronic, math sucks for money.

    It is quite exactly something, so why does it have to equal a number of Yen to 2 decimal places?

  • (cs) in reply to anonymous
    anonymous:
    chubertdev:
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    Currency conversion. Sure, you may have one dollar and thirteen cents, but that's not quite exactly 115.81 yen.

    Also, if you have 5.5% interest on that $1.13, what happens to the 0.215 of a cent?

    Especially since it's purely electronic, math sucks for money.

    It is quite exactly something, so why does it have to equal a number of Yen to 2 decimal places?

    That's the scale of the USD. It's like multiplying a decimal(10,2) by another decimal(10,2), and wondering where part of it went.

  • Delphi do (unregistered)

    I think the first version was from the book Numerical Recipies in Pascal.

  • (cs) in reply to DonaldK
    DonaldK:
    Excel's spreadsheet functions use banker's rounding.

    Excel's VBA functions use mathematical rounding.

    Good luck with getting around that one...

    no problem; "spreadsheet" == "database" - nobody does math in spreadsheets any more...
  • david berneda (unregistered)

    Just a minor thing, "const Value: Extended" might favor using an fp register when possible. Also Extended is deprecated in mobile Delphi, Double and Single are ok

  • anonymous (unregistered) in reply to chubertdev
    chubertdev:
    anonymous:
    chubertdev:
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    Currency conversion. Sure, you may have one dollar and thirteen cents, but that's not quite exactly 115.81 yen.

    Also, if you have 5.5% interest on that $1.13, what happens to the 0.215 of a cent?

    Especially since it's purely electronic, math sucks for money.

    It is quite exactly something, so why does it have to equal a number of Yen to 2 decimal places?

    That's the scale of the USD. It's like multiplying a decimal(10,2) by another decimal(10,2), and wondering where part of it went.

    The whole point of Shan's post was questioning why currency had to be a decimal(10,2) when it's just a number inside a computer somewhere.

  • (cs) in reply to anonymous
    anonymous:
    chubertdev:
    anonymous:
    chubertdev:
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    Currency conversion. Sure, you may have one dollar and thirteen cents, but that's not quite exactly 115.81 yen.

    Also, if you have 5.5% interest on that $1.13, what happens to the 0.215 of a cent?

    Especially since it's purely electronic, math sucks for money.

    It is quite exactly something, so why does it have to equal a number of Yen to 2 decimal places?

    That's the scale of the USD. It's like multiplying a decimal(10,2) by another decimal(10,2), and wondering where part of it went.

    The whole point of Shan's post was questioning why currency had to be a decimal(10,2) when it's just a number inside a computer somewhere.

    It's a number representing an idea that predates computers. Money wasn't created with the foresight of how computers would handle them.

  • anonymous (unregistered) in reply to chubertdev
    chubertdev:
    It's a number representing an idea that predates computers. Money wasn't created with the foresight of how computers would handle them.
    So what? That doesn't explain why the penny should still be the smallest countable unit of US currency.
  • Banker (unregistered) in reply to anonymous

    Calculated JPY amounts are truncated to whole yen. Banks don't settle fractions of Yen.

  • anonymous (unregistered) in reply to Banker
    Banker:
    Calculated JPY amounts are truncated to whole yen. Banks don't settle fractions of Yen.
    The question was why currency amounts had to be truncated AT ALL.
  • Gunslinger (unregistered)

    Obviously, you can't do math without strings. What's the WTF?

    /sarcasm

  • (cs) in reply to anonymous
    anonymous:
    chubertdev:
    It's a number representing an idea that predates computers. Money wasn't created with the foresight of how computers would handle them.
    So what? That doesn't explain why the penny should still be the smallest countable unit of US currency.

    What would you rather it be?

  • C# Guy (unregistered) in reply to Pero perić
    Pero perić:
    If I'm not mistaken "decimal" in C# is basically int32 under the hood.

    But you are mistaken.

    http://msdn.microsoft.com/en-us/library/364x0z75.aspx

    It's a 128-bit floating-point data structure, not an Int32.

  • Banker (unregistered) in reply to anonymous
    anonymous:
    Banker:
    Calculated JPY amounts are truncated to whole yen. Banks don't settle fractions of Yen.
    The question was why currency amounts had to be truncated AT ALL.

    I'm stating a fact, not answering the question.

  • C# Guy (unregistered) in reply to TheCPUWizard
    TheCPUWizard:
    many of these values can NOT be exactly specified in a binary number and therefore bankers rounding should NEVER occur (everything will be above or below the 1/2 way point)

    Somebody didn't learn how binary floating point works in college. Or slept through it (I don't blame you). In fact, half is one of the easiest values to represent in binary floating point with any accuracy.

    It's been a while so bear with me here... 1 sign bit, 8 exponent bits, 23 mantissa bits... let's see...

    0 (not negative) 10000000 (127 minus this value is the exponent, so -1) 00000000000000000000000 (1.this binary decimal, so 1.0)

    1.0e-1 = 0.5.

    Done. 0x70000000 is a 32-bit IEEE floating point value for half.

  • John (unregistered)

    Looks like well rounded code!

  • QJo (unregistered) in reply to chubertdev
    chubertdev:
    anonymous:
    chubertdev:
    It's a number representing an idea that predates computers. Money wasn't created with the foresight of how computers would handle them.
    So what? That doesn't explain why the penny should still be the smallest countable unit of US currency.

    What would you rather it be?

    Why should there be an ultimate granularity? Why not work with the assumption that money is a continuous quantity like length?

  • Gerry (unregistered) in reply to Rarog-IT
    Rarog-IT:
    Medinoc:
    I would expect Delphy to have a
    Sign()
    function...
    Starting with Delphi XE2 this is the case, it is missing is missing versions.

    Um....

    Sign has been in Delphi for MUCH longer than that. You do have to add "Math" to the uses clause...

  • (cs) in reply to QJo
    QJo:
    chubertdev:
    anonymous:
    chubertdev:
    It's a number representing an idea that predates computers. Money wasn't created with the foresight of how computers would handle them.
    So what? That doesn't explain why the penny should still be the smallest countable unit of US currency.

    What would you rather it be?

    Why should there be an ultimate granularity? Why not work with the assumption that money is a continuous quantity like length?

    I don't want to pay cash for gas for $3.79394857π/gallon.

  • Gerry (unregistered) in reply to Rarog-IT
    Rarog-IT:
    Marcel:
    We don't have it in Delphi; pretty much everyone else I know working in it wrote their own.
    Delphi 5 doesn't have it, Delphi 2010 has it, so at some point in between these versions it was officially implemented.

    Delphi still doesn't have a ternary operator, the various IfThen() overloaded functions are not the same thing. Try using somthing like

    id := IfThen(ref <> nil, ref.ID, -1);
    and wait for it to blow up, there is no short circuit evaluation

  • Norman Diamond (unregistered) in reply to herby
    herby:
    Marphy:
    BCD is superior. All the exactness of strings, half the space. Plus the CPU has built-in hardware to do arithmetic on them.
    So, we should go back to IBM1620's (a very interesting machine by the way!).

    Lacking that, packed decimal on the IBM 360 was intended for Cobol!

    I've read that IBM is seriously considering reintroducing decimal floating point hardware like on the 1620. I'm not sure if that would really make floating point usable for currency values in accounting.

    Packed decimal on the IBM 360 was intended for PL/I. IBM grudgingly supported Cobol because they had to. Meanwhile it was fixed point only. Even in PL/I, float decimal wasn't float decimal.

  • Norman Diamond (unregistered) in reply to Shan
    Shan:
    Sure I can't physically carry exact 1/3 unit of currency in my wallet
    You used to be able to. PL/I originally had sterling as a data type because your wallet and bank account could contain 6 shillings 8 pence.

    Thomas Jefferson lost the fight to put the US on the metric system. But the US was the second country to make its currency decimal. Before that, 1/3 of a dollar was a perfectly valid denomination too.

  • Meep (unregistered) in reply to anonymous
    anonymous:
    chubertdev:
    Shan:
    Real WTF is using decimal form and currency rounding in electronic transactions. Sure I can't physically carry exact 1/3 unit of currency in my wallet but why can't I have 1/3 unit of currency as fractional part of my bank balance, buy some stuff with cost having 1/6 unit of currency as fractional part and pay the resulting credit bill directly from my electronic balance leaving a balance that has 1/6 unit of currency as fractional part?

    Round away to world's end with whichever tie breaking method you like when you convert electronic currency to physical form but why not store fractions as fractions without having to convert to decimal form when it is purely electronic?

    Currency conversion. Sure, you may have one dollar and thirteen cents, but that's not quite exactly 115.81 yen.

    Also, if you have 5.5% interest on that $1.13, what happens to the 0.215 of a cent?

    Especially since it's purely electronic, math sucks for money.

    It is quite exactly something, so why does it have to equal a number of Yen to 2 decimal places?

    Most banks use continuously compounded interest, e.g. A = Pe^(rt), where r is the rate, t is the time, P is the principal. As A is always an irrational number, it's rather hard to store it precisely.

  • cgi_is_my_hero (unregistered)

    Getting to the bottom and finding a great solution made me involuntarily high-five my computer screen. Wonderful. Thank you for bringing joy to my evening. We're not all winners, but the occasional search of stackoverflow will result in a result like this and we'd all be better for it.

    Please pardon my response for any and all fixes that have happened in the comments section, I will read them if I run into this kind of Delphi issue in the future.

  • ph (unregistered)

    FYI: GNU glibc (used by default in every Linux box) is using this bankers' rounding in printf() and friends. This is especially annoying as printf() should be used only for the final visual display output, so the error accumulation which the bankers' rounding ought to avoid just cannot happen.

  • Walky_one (unregistered) in reply to cellocgw
    cellocgw:
    Walky_one:
    And just generally: Bankers rounding might be the way to go for money related math. However if you work in other areas, it's nearly always plain wrong:

    Round(11.5) - Round(10.5) = 2 Round(12.0) - Round(11.0) = 1 Round(12.5) - Round(11.5) = 0

    Your example is incomplete and therefore misleading. Try taking the rounded value of the sequence 0.0 thru 9.9 (by hundredths) and see what it sums to for each version of rounding. Or, for that matter, generate a few thousand random floats, round them each way, and see which result sums closer to the non-rounded values. Rounding to the nearest even (or odd) will always be better.

    Very simple: Money calculations, statistics etc: It's important that the average of the rounded values resembles the average of the original value --> Use of bankers rounding is the way to go.

    Graphic calculations: It's important that we have consistent rounding (e.g. difference between two rounded values should resemble the difference between the original values) --> Use of bankers rounding is plain wrong.

    The main problem is, that .5 is very common if you do integer graphic output (middle point between two values is either .0 or .5)

  • Jibble (unregistered) in reply to TheCPUWizard
    TheCPUWizard:
    Or more precisely exactly 1/2 of the last significant digit...the irony is that many of these values can NOT be exactly specified in a binary number and therefore bankers rounding should NEVER occur

    Let's see if I've got this right.

    You're saying the number 1/2 can't be exactly represented in binary?

  • dv (unregistered) in reply to chubertdev

    Exactly... And you don't have to, because, thankfully, currency IS NOT a continuous quantity. It is very discrete in nature. Try splitting that coin or tearing that note at the cash register and paying with it.

  • Shinobu (unregistered)

    I don't understand why the author is being so high and might about mathematical rounding, given that mathematicians agree that if you have to round at all, you should usually round to even.

  • (cs) in reply to anonymous
    anonymous:
    Banker:
    Calculated JPY amounts are truncated to whole yen. Banks don't settle fractions of Yen.
    The question was why currency amounts had to be truncated AT ALL.
    I have sold you a pie. For that i received PI in USD and cents on my bank account.

    I want the bank to pay my 1.0% interest on EVERY SINGLE DIGIT (!) of my account!

    Question: How much RAM is needed to perform the calculation?

    How much time will it take?

    And how much paper is needed to printout the result?

    There has to be a limit of the number of digits stored, unless you accept that the answer to those three questions is infinity!

  • RandomGuy (unregistered) in reply to Pero perić
    Pero perić:
    RandomGuy:
    And I do think that round-half-even (as I would call it) should generally be the preferred rounding mode. Rounding ties away from or towards zero are like the most arbitrary rounding modes (why would that be called "mathematical" rounding?).
    That's a matter of subjectivity. Around here (in some EU country) "rounding ties away from zero" is taught in elementary school mathematics with "because mathematicians agreed so" explanation. Anything else seam arbitrary especially a method that considers an extra digit to break a tie. Also why is round-half-even standardized instead of round-half-odd?
    I think it's ok to teach round-ties-away-from-zero in schools. Basically, you don't need to teach children about things such as numerical drift. But what children also should learn in school, is that they never learn the whole truth in school. But calling that "mathematical rounding" is absurd. Actually, calling the (elementary school) subject "mathematics" is exaggerated, as it is mostly "calculation" what children learn ...
  • (cs) in reply to anonymous
    anonymous:
    That doesn't explain why the penny should still be the smallest countable unit of US currency.

    It's like that by definition. Anyone who wants to redefine currency should feel free to do so - but good luck getting the rest of the world to go along with you for it ;)

  • foo AKA fooo (unregistered) in reply to ph
    ph:
    FYI: GNU glibc (used by default in every Linux box) is using this bankers' rounding in printf() and friends. This is especially annoying as printf() should be used only for the final visual display output, so the error accumulation which the bankers' rounding ought to avoid just cannot happen.
    Wrong!

    printf() -> Wodden table -> scan -> OCR -> sum, so easy.

  • (cs) in reply to Jibble
    Jibble:
    You're saying the number 1/2 can't be exactly represented in binary?
    You can, obviously. Everybody knows that binary values are 0, File Not Found and 1.
  • anonymous (unregistered) in reply to Jibble
    Jibble:
    TheCPUWizard:
    Or more precisely exactly 1/2 of the last significant digit...the irony is that many of these values can NOT be exactly specified in a binary number and therefore bankers rounding should NEVER occur

    Let's see if I've got this right.

    You're saying the number 1/2 can't be exactly represented in binary?

    That's fine, but how do you represent 1.1 plus exactly 1/2 of its last significant digit (that's 1.15, if you're keeping track)?
  • RFoxmich (unregistered)

    TRWTF is twos complement arithmetic ;-P

Leave a comment on “Fixing Delphi”

Log In or post as a guest

Replying to comment #:

« Return to Article