- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Had to write something pretty similar a while back... round to the nearest amount that could be paid in cash (e.g. 5c or 10c, depending on currency). And it might not have been the best way to do it, but it certainly didn't use hardcoded ranges in if blocks, and it certainly didn't use floating-point...
Admin
double frist!
Admin
To round to nearest quarter, you can multiply by 4, round, then divide by 4. Yes it's bad code, but not hysterically funny bad code.
Admin
Using doubles for financial calculations is problematic on its own, because binary floating point cannot exactly represent decimal values.
Admin
"You just know all the currency values all through this application are doubles."
It's scary just how many systems do this. And it's doubly inexcusable in .NET, which has a decimal type specifically designed for currency values and other values that must be handled with fixed precision.
Admin
So, remind me again, why is everyone converting their financial code from COBOL (which has first-class decimal numbers) into Java or C#?
Admin
I will give a different result, if fractions of cents are used. 0.128 --> 0.00 in the original code 0.128 --> 0.25 in your solution
The first case is probably correct! Rounding in financial systems does not always make sense.
Admin
That and when you have a high enough value in a floating point, you lose the least significant digits. Sure, losing track of a few meters when measuring the solar system is not such a big deal (unless it is time for an orbital insertion), but losing a few dollars out of a billion will mean that your trial balance will never actually balance ever again. VBA has the Currency type; .Net has the Decimal type. Both allow you to keep track of fractions of cents while still having billions of dollars/euros/pounds, etc. in full precision.
Admin
This. So much this.
Admin
Unfortunately, C++ doesn't have a built-in fixed-point type. So when you're the CTO of a fin-tech startup, and you decide to build your backend in C++ because that's what you know, who's got time to muck around with defining new types? We'll just go ahead and use doubles because we're not dealing with very big numbers anyway...
Yes, this was one of my former employers.
Admin
Could this code be old enough to have started as C++? I don't recall C++ having a built-in decimal/money type 20 years ago. Although I'd use System.Decimal by default for money these days, maybe they don't deal in fractional cents or numbers high enough to shift the point.
TRWTF is rounding to quarters. I had a part-time job where they rounded to nickels and thought that was stupid too.
Also, the fix for this should include a date parameter to prevent corrupting old data with different calculations. Doubly recommending if you don't want to touch the code at all. Having to change anything else anywhere else sets off many PMs' "too risky!" alarm. Better to let the corruption trigger hundreds of tickets in the future, long after you've forgotten about the calculation change that caused it...
Admin
The problem with using Decimals or whatever is that there's a fairly widespread understanding that they're "slow" compared to doubles.
There's no corresponding widespread understanding of what exactly "slow" means, so you get a bunch of people saying "THEIR SLOW!!!1" without doing their own benchmarking on their own system, and thus they stick to doubles without seeing if they need to.
Admin
I once worked for a large multinational company that stored and calculated their dollars using, I forget, 32 or maybe 64-bit reals. Nobody raised a fuss.
Admin
You mean: 'They're slow', right?
Admin
Luckily, doubles have better precision than cents on a billion (2^52 is about 4x10^15). So the problem that is most likely to come up is the inexact representation of decimal fractions.
Admin
I'll bite. What is the right way to round to a quarter?
Admin
TRWTF is he started working for Initech. Didn't he read yesterday's story in which Initech announced it was shutting down?
Admin
Personally, I don't even worry about fixed point for currency.
Currency always goes in an integer type, it is expressed in the smallest monetary units. (Thus it's not $1, but 100 cents.)
Note that this isn't enough to avoid all problems. You have the contract price, accounting wants to split this into a base charge and use tax. The CFO goes ape when he finds "Use tax roundoff correction, $.01" in the details of a job. Sorry, there sometimes isn't a solution to the equation.
Admin
Rounding fractions incorrectly could be potentially very lucrative. I seem to remember a story about someone moving all the very tiny fractions to his own account. Only greed made him go on too long so he got caught.
Come to think of it, isn't Office Space based on that and what was that company called again?
Admin
For an amount of a billion dollar, double precision rounding error is less than one in 320,000 cents. Double + knowing what you are doing beats decimal + clueless any time.
Admin
Superman 3.
Admin
Because all the people who really know COBOL and want to do COBOL are dying. Incidentally, if you don't mind working on stupefyingly boring yet terrible legacy systems (which have no documentation) in COBOL, learning it would be a great way to guarantee well paid employment effectively forever.
And C# has a built-in decimal type, so that's not a problem - at least if you actually use it. Java's got DecimalFormat but I've never used it so don't know if it works right for this application.
Admin
BigDecimal is an arbitrary precision integer and a scale (a power of 10). So yes, highly suitable for financial applications (and irrelevant for my work, which uses more transcendental functions than you can shake a stick at and so needs… more advanced techniques).
Admin
If it were well-paid, they'd have no problem finding people. They want H1Bs, though, so they're limited to what H1Bs have heard of and want to sell. Some organizations have been so hollowed out by offshoring that seven layers of management don't have any practical experience to the point you may as well consult a magic 8 ball.
Senior VP: "Well, if you think we program that rocket in JavaScript, that's all I need to hear." Senior VP (aside): "What's JavaScript? What's programming? What's a rocket?"
Admin
Using the wrong "their" was part of the joke yes. You'll notice I did it correctly in the previous line.
Admin
The first StackOverflow result I see for "round to fraction" uses the "multiply, round (or floor or etc), divide" method Mr. TA suggested above, which works for rounding to any fraction. I'd say there's not much better.
Admin
As noted above, if fractional cents are in the input data, this will return some nonobvious results. Twelve and six tenths cents will be rounded down to zero. The "obvious" multiply-floor-divide method would round it up to one quarter.
Which one is "right" is entirely up to the business, and I am sure there are businesses which will fight fiercely for either choice.
Addendum 2019-04-30 15:59: EDIT: I meant "multiply-round-divide". "multiply-floor-divide" would be yet a third set of results.
Admin
You've obviously never worked in finance, where prices sometimes go to eight decimal places...
Admin
ToStr/string functions/ToInt32 is the new NEW math! Also, I hope none of their customers are big enough to be worth more than than $2B.
I like that extra zero digit of precision, to make sure we're REALLY exact! Inaccurate floating point representation? What's that? This is double so it's super precise! At least those are the only four values of cents that have an exact floating point representation. I can't say so much for the other 96 values.
To be fair, using double wouldn't be so bad if it represented a number in pennies. I don't think I want to see the string function that would need.
But quarters are already round, you silly person! Have you ever seen a square quarter?
Also, in the oil lease industry, ownership fractions are represented as a number between 0.0 and 1.0, with 8-10 digits after the decimal point. And that fraction likely has two or three digits * after * the decimal point. I'm sure that does fun things to the math that divides up royalties.
Admin
Doubles can exactly represent decimal values. You need to use at least one more bit than the fractional bit required by your decimal representation, and you need to understand what you are doing.
Not too fine a point on it, if you have the idea that doubles can't exactly represent decimal values, then you are exactly the kind of person that should NOT use doubles to represent decimals, because you /think that/ you understand floating point numbers.
Admin
At least doubles can exactly represent multiples of 0.25 (2^-2), up to a certain value.
Admin
You can not represent 0.1 in binary, which is what doubles use. It works out 0.000110011 recurring, so you can not use one more bit then the infinite bits required.
Admin
Actually you can as long as you know you will only ever have numbers with a certain maximum number of fraction digits. So, if you have a value in dollars and you know you will never have to deal with fractions of a cent, you simply store the number of cents (i.e. multiply the dollar value by 100). You can use a double to do that, but a 64 bit integer might be better.
You still have to deal with rounding errors. For example, you have to know that dividing $1.01 between three people will leave you with a missing cent if you are not careful.
Admin
To whoever mentioned "multiply by 4, round, then divide by 4" method, you still have to deal with the flooring vs ceiling conundrum.
The switch and case method is actually not totally bad at all. It is easy to understand and manageable. Sometimes that has more value than elegance.
Admin
It's also in Superman 3.
Admin
Just run 1*(.5-.4-.1) in Excel to see a nice rounding error.
Using floats/doubles for calculation can fairly fast give errornous answers.
Cobol and most system written in it has sorted out how rounding and calculations should be done. Interest rates are 3 decimals, exchange rates usually 5 decimals and rounding isn't really done because you just truncate the values.
If you do deliberate rounding the amount is booked on a rounding account and keept track of.
Admin
A better question is, why doesn't Java still have an unlimited native integer type? (Of course I am nearly sure there are third party libraries to do that)
Addendum 2019-05-07 01:42: Oops, I stand corrected. BigDecimal, apparently.
Admin
Don't know if anyone mentioned this already, but I hope that input is never a negative value.
RoundToQuarter(-1.99);
Gives -1.00