15 ms·
It's actually in use in many places, for things like handling currency and money, and for when you get funny corner cases involving rounding such numbers and po
by loopz 7y ago
It's actually in use in many places, for things like handling currency and money, and for when you get funny corner cases involving rounding such numbers and pooling the change.
Whenever I see someone handling currency in floats, something inside me wither and die a small death.
- recursive 7y agoCurrency handling is almost never done with rationals (numerator and denominator) and is frequently (and correctly so!) done with fixed or floating point decimal types.
- larrik 7y agoThis is false. It's not correct to handle currency with floating point types.
- simias 7y agoThey said floating point decimal types which probably means BCD.
- recursive 7y agoThere are different implementations, and BCD is only one of them. Another popular one is a mantissa and exponent, but the exponent is for a 10-based shift rather than the typical floating point.
- anonydsfsfs 7y agoIEEE 754 defines decimal floating point: https://en.wikipedia.org/wiki/Decimal_floating_point#IEEE_754-2008_encoding https://en.wikipedia.org/wiki/Decimal_floating_point#IEEE_75...
- jhj 7y agoTbey mean radix-10 floating point, as compared to the radix-2 floating point you are thinking of. The packing of the decimal fractional digits in the significand of a radix-10 FP number need not be in BCD, it can use other encodings (e.g., DPD or something else). 0.3 is exactly representable in radix-10 floating point but not radix-2 FP (would be rounded to a maximum of 0.5 ulp error as seen in the title), for instance, just as 1/3 = 0.3333... is exactly representable in radix-3 floating point but neither radix-2 or radix-10 FP, etc.
- usr1106 7y agoRight, it is not correct. But many programs do it wrong. If you just do a couple of additions the problem will never be noticed. It's easy to write a program that sums up 0.01 until the result is not equal to n * 0.01. Not at my computer now, so I can't do it again. I remember n was bigger to be relevant for any supermarket cashier. But of course applications exist where it matters.
- recursive 7y agoBut it is correct. > It's easy to write a program that sums up 0.01 until the result is not equal to n * 0.01. It's not easy to do that if you use a floating point decimal type, like I recommended. For instance, using C#'s decimal, that will take you somewhere in the neighborhood of 10 to the 26 iterations. With a binary floating point number, it's less than 10.
- Thiez 7y agoThat is simply not true. The C# decimal type doesn't accumulate errors when adding, unless you exceed its ~28 digits of precision. E.g. see here: https://rextester.com/RMHNNF58645 https://rextester.com/RMHNNF58645
- recursive 7y ago> unless you exceed its ~28 digits of precision Precisely. That's why I specified ~ 10^26 addition operations.
- usr1106 7y agoOf course with a decimal type there is no rounding issue. That's not what 0.30000000000000004 is about. Many languages have no decimal support built in or at least it is not the default type. With a binary type the rounding becomes already visible after 10959 additions of 1 cent. #include <stdbool.h> #include <stdio.h> #include <string.h> bool compare(int cents, float sum) { char buf[20], floatbuf[24]; int len; bool result; len = sprintf(buf, "%d", cents / 100 ) ; sprintf(buf + len , ".%02d" , cents % 100 ) ; sprintf(floatbuf, "%0.2f", sum) ; result = ! strcmp(buf, floatbuf) ; if (! result) printf( "Cents: %d, exact: %s, calculated %s\n", cents, buf, floatbuf) ; return result; } int main() { float cent = 0.01f, sum = 0.0f; for (int i=0 ; compare(i, sum) ; i++) { sum += cent; } return 0; } Result: Cents: 10959, exact: 109.59, calculated 109.60 This is on my 64 bit Intel, Linux, gcc, glibc. But I guess most machines use IEEE floating point these days so it should not vary a lot.
- epse 7y agoIt's not correct, but in many cases it's plenty accurate
- jsjohnst 7y agoIf you are dealing with other people’s money, the only accurate is accurate. Close enough should not be in any financial engineer’s mindset, imho.
- recursive 7y agoIn this case, it's both. Decimal floating point types do not lose precision with base-10 numbers, unless using trig, square roots, arbitrary division and the like.
- jsjohnst 7y ago> arbitrary division Like commonly happens doing financial calculations, especially doing interest calculations.
- recursive 7y agoI don't see any problem with it if it's decimal. Here's an accepted answer on stack overflow with hundreds of upvotes recommending the use of `decimal` to store currency amounts in C#. That's a decimal floating point type. https://stackoverflow.com/a/693376/44743 https://stackoverflow.com/a/693376/44743
- deleted 7y ago[deleted]
- yellowapple 7y agoIt's not correct, but it happens anyway, even in large ERP systems that really should know better but somehow don't.
- recursive 7y agoIt is correct! Using decimal types is the widely recommended way of solving this problem. That includes fixed and floating point types. The problem is using base-2 floating point types, since those are subject to the kinds of rounding errors in the OP. But decimal floating point types are not subject to these kinds of rounding errors. But they still can't precisely represent quantities like 1/3 or pi.
- yellowapple 7y ago> Using decimal types is the widely recommended way of solving this problem. No, it's not. The widely recommended way of solving this problem is to use fixed-point numbers. Or, if one's language/platform does not support fixed-point numbers, then the widely recommended way of solving this problem is to emulate fixed-point numbers with integers. There is zero legitimate reason to use floating-point numbers in this context, regardless of whether those numbers are in base-2 or base-10 or base-pi or whatever. The absolute smallest unit of currency any (US) financial institution is ever likely to use is the mill (one tenth of a cent), and you can represent 9,223,372,036,854,775,807 of them in a 64-bit signed integer. That's more than $9 quadrillion, which is 121-ish times the current gross world product; if you're really at the point where you need to represent such massive amounts of money (and/or do arithmetic on them), then you can probably afford to design and fabricate your own 128-bit computer to do those calculations instead of even shoehorning it onto a 64-bit CPU, let alone resorting to floating-point. Regardless of all that, my actual point (pun intended) is that there are plenty of big ERP systems (e.g. NetSuite) that use binary floating point numbers for monetary values, and that's phenomenally bad.
- lmilcin 7y agoI develop accounting software for banks, brokerage houses and likes. Currency, taxes, rebates, etc. handling is NEVER done with floating point. Whatever you do with money you need predictable, reproducible results. It is norm that calculations are checked by software at two companies on both sides of transaction. Any discrepancies are alarms, bug reports, unhappy customers. Every significant operation is exactly specified with rounding rules, etc. For card payments and especially on terminals usually BCD is used. For everything else usually some kind of arbitrary length decimal library (BigInteger, BigDecimal).
- chrisseaton 7y ago> Currency, taxes, rebates, etc. handling is NEVER done with floating point. Nonsense. I’ve seen real banking code at reputable banks that uses floats. > Whatever you do with money you need predictable, reproducible results. Floats aren’t random. They’re perfectly deterministic, predictable and reproducible. If you do the same operation in two places you get the same result.
- mynegation 7y agoI write real banking code. There is definitely a banking code that uses floats, e.g. valuation of financial instruments. The parent comment talks about software that does transactions and “simpler” calculations, like taxes and fees etc. When people talk about non-determinism of floating point, what they usually mean is non-associativity, that is (x+y)+z may not be exactly equal to x+(y+z).
- throwawaymath 7y ago> When people talk about non-determinism of floating point, what they usually mean is non-associativity, that is (x+y)+z may not be exactly equal to x+(y+z). Good example of this, in Python 3: >>> (0.1 + 0.2) + 0.3 0.6000000000000001 >>> 0.1 + (0.2 + 0.3) 0.6
- chrisseaton 7y ago
- randomidiot666 7y agoYou're confusing foreign exchange conversion with accounting arithmetic. Two different things.
- _bxg1 7y agoI haven't worked in fintech but I've read that money is often represented (at least in storage) as plain integers, since for example US currency only ever goes to two decimal places. But I guess once you start operating on it you run into potential truncation unless you use rationals.
- bwilliams18 7y agoUS currency can go to more than two decimal places... http://blogs.reuters.com/ben-walsh/2013/11/18/do-stocks-really-trade-for-fractions-of-a-penny-sort-of/ http://blogs.reuters.com/ben-walsh/2013/11/18/do-stocks-real... I guess it's time for someone to write an "Assumptions Programmers make about money" post.
- LanceH 7y ago1. Money in a brokerage account is not US currency.
- zrobotics 7y agoWhile it isn't physical US currency, my brokerage account represents the value of the account in units of USD- therefore any rules about how US currency works should apply. Additionally, fractional cents are often presented to the consumer when purchasing gas/fuel.
- nitwit005 7y agoNo it can't. There are systems that track things worth less than a penny for later billing, but at the end of the month when they bill someone, they do some sort of rounding.
- ars 7y agoIf you are earning interest at a bank, and you've earned a fraction of a penny, they will eventually pay it to you once you've earned enough for a whole penny. i.e. they track your account balance to more than 2 digits, they just only show you 2 digits.
- mumblemumble 7y agoIt's not always terrible. I've seen doubles appropriately used in cases where performance was paramount, and floating point error was either not relevant or less important. That said, yeah, when working with money in situations where money matters, some sort of decimal or rational datatype should be the rule, not the exception.
- nothrabannosir 7y agoStoring money in floating point is always terrible. If speed is an issue, store it in integer types representing the smallest unit in the currency, e.g. pennies. Unless you’re doing, what, massively parallel GPU algos on batches of independent amounts? But even then you could use the float as an int in that way... Honestly when is float ever actually good for money? Not for speed, not for correctness, ...
- stock_toaster 7y ago> If speed is an issue, store it in integer types representing the smallest unit in the currency, e.g. pennies More typically, mills[1] (tenth of a cent). [1]: https://en.m.wikipedia.org/wiki/Mill_(currency) https://en.m.wikipedia.org/wiki/Mill_(currency)
- deleted 7y ago[deleted]
- DonHopkins 7y agoAmazon's EC2 hourly prices are rounded to mils ($0.011/hour). https://aws.amazon.com/emr/pricing/ https://aws.amazon.com/emr/pricing/ Azure has some hourly prices with ten-thousandths of a cent ($0.0102/hour): https://azure.microsoft.com/en-ca/pricing/details/virtual-machines/windows/ https://azure.microsoft.com/en-ca/pricing/details/virtual-ma... Microsoft should use gas station 9/10 pricing conventions to just barely undercut Amazon's lowest price $0.011 with $0.0109. https://www.marketplace.org/2018/10/11/why-do-gas-prices-end-910-cent/ https://www.marketplace.org/2018/10/11/why-do-gas-prices-end... >“They found out that if you priced your gas 1/10 of a cent below a break point, let’s say 40 cents a gallon, ‘.399’ just looked to the public like 39 cents…”
- jrochkind1 7y ago> It's actually in use in many places, for things like handling currency and money Hm, are you sure? I don't believe "rational" types which encode numbers as a numerator and denominator are typically used for currency/money. If they were, would the denominator always be 100 or 1000? I guess you could use a rational type that way, although it'd be a small subset of what rational data types are intended for. But I guess it'd be "safe"? Not totally sure actually, one question would be if rounding works how you want when you do things like apply an interest percentage to a monetary amount. (I am not very familiar with rational data types, and am not sure how rounding works -- or even if they do rounding at all, or just keep increasing the magnitude of the denominator for exact precision, which is probably _not_ what you'd want with currency, for reasons not only of performance but of desired semantics). You are correct an IEEE-754 floating point type is inappropriate for currency. I believe for currency you would generally use a fixed-point type (rather than floating point type), or non-IEEE "arbitrary precision floating point" type like ruby's BigDecimal (ruby also offers a Rational type. https://ruby-doc.org/core-2.5.0/Rational.html https://ruby-doc.org/core-2.5.0/Rational.html . This is a different thing than the arbitrary-precision BigDecimal. I have never used Rational or seen it used. It is not generally used for money/currency.) Or maybe even a binary-coded decimal value? (Not sure if that's the same thing as "arbitrary-precision floating point" of ruby's BigDecimal or not). There are several possible correct and appropriate data encodings/types for currency, that will have the desired precision and calculation semantics... I am not sure if rational data type is one of them, and I don't believe it is common (and it would probably be much less performant than the options taht are common). Postgres, for instance, does not have a "rational" type built in, although there appears to be a third-party extension for it. Yet postgres is obviously frequently used to store currency values! I believe many other popular rdbms have no rational data type support at all. I'm not actually sure what domains rational data types are good for. Probably not really anything scientific measurement based either (the IEEE-754 floating point types ARE usually good for that, that is their use case!) The wikipedia page sort of hand-wavily says "algebraic computation", which I'm not enough about math to really know what that means. I have never myself used rational data types, I don't think! Although I was aware of them; they are neat.
- loopz 7y agoGood catch! I'm thinking of fixed-point number types. Ruby's Rational was/is cool, but looks like an inherently difficult number-type to work with and keep sanity high. For currency, business side should decide the rules (* 100 or * 1000000), and where to funnel the pennies ;) Fixed-point has it's own sort of gotchas, ie. multiplication, power, division, sqrt, etc. So there are fancy techniques to work with the numbers, like https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
- mehrdadn 7y ago> It's actually in use in many places, for things like handling currency and money Which specific places have you seen it used in?
- zelly 7y agoCurrency in banking is handled with bigints. Not rationals, just bigints of the smallest unit (i.e., 1 cent). This forces you to order operations so that divisions are done last or not at all.
- otabdeveloper4 7y agoThe bigint you describe is just a poor man's rational, given that no computer architecture or mainstream language support them natively.
- boomlinde 7y agoWell, you could look at it as x/100 rationals representing a dollar value. But you could also look at it as an integer amount of a smaller unit (cents). The difference is insignificant; computers support it natively.
- zelly 7y agoThere is a slight difference where it forces decision-making about rounding at every step. Most importantly, it makes errors that print money (or burn money) impossible states. I'm aware of BigRational libraries or more clever currency abstractions, but this is the least magical way of doing it, kind of like representing datetimes as 64-bit UNIX epochs.
- Maultasche 7y agoWhen I was in college the professor of my software engineering class explicitly warned us to never use floating point numbers for money. He went on at length of the dangers of floating points for dealing with money and warned us that people can get really upset if they feel like they've been screwed out of money. He had decades of experience in the software development industry and I got the feeling that he'd seen the effect of this issue personally. I still remember that warning well.
- alfalfasprout 7y agoWell, this is one of those things where context matters. In trading, it's super common to use floating point arithmetic for decision logic since it's very fast and straightforward to write. The actual trade execution, however, almost always relies on integer arithmetic because then money is actually being used (and hence must be tracked properly). It's not therefore inherently incorrect to do currency conversions with floats in some situations provided that the actual transaction execution relies on fixed precision or decimal arithmetic.
- kccqzy 7y ago> Whenever I see someone handling currency in floats, something inside me wither and die a small death. Meh. When used correctly in the right circumstances it is acceptable to use floats. Here's an example. Suppose you are pricing bonds, annuities, or derivatives. All the intermediate calculations make essential use of floating point operation. The Black–Scholes model for example requires the logarithm, the exponential, the square root, and the CDF of the normal distribution. None of that is doable without floats. Even for simpler examples it is sometimes okay to use floats. If you only ever need to store an exact number of cents, you can totally store the number of cents in a double. Integer operations are exact using IEEE-754 double operations when they are smaller than 2^53-1 or so. There's usually no benefit of doing so, but hey it's possible.