32

I was watching http://www.joelonsoftware.com/items/2011/06/27.html and laughed at Jon Skeet joke about 0.3 not being 0.3. I personally never had problems with floats/decimals/doubles but then I remember I learned 6502 very early and never needed floats in most of my programs. The only time I used it was for graphics and math where inaccurate numbers were ok and the output was for the screen and not to be stored (in a db, file) or dependent on.

My question is, where are places were you typically use floats/decimals/double? So I know to watch out for these gotchas. With money I use longs and store values by the cent, for speed of an object in a game I add ints and divide (or bitshift) the value to know if I need to move a pixel or not. (I made object move in the 6502 days, we had no divide nor floats but had shifts).

So I was mostly curious.

5
  • 10
    because it matters very much that the interest i pay on my mortgage remains 12.6 and doest become 13 just cos 13 is a such a nice round number.
    – Chani
    Commented Jun 28, 2011 at 7:14
  • 2
    "I learned 6502 very early and never needed floats in most of my programs...for speed of an object I add ints and divide the value to know whether to move a pixel or not." These are very unusual ways to accomplish these tasks in modern practice, except for representation of money as long cents.
    – jprete
    Commented Jun 28, 2011 at 15:14
  • Good thing computer understand millicents.
    – tylermac
    Commented Jun 29, 2011 at 17:28
  • 1
    Or in addition, why use decimals when we can use fractions?
    – tylermac
    Commented Jun 29, 2011 at 17:37
  • 6
    @Scrooge - ironically you can't represent 0.6 in a float. Commented Jun 30, 2011 at 15:24

13 Answers 13

30

Because they are, for most purposes, more accurate than integers.

Now how is that? "for speed of an object in a game..." this is a good example for such a case. Say you need to have some very fast objects, like bullets. To be able to describe their motion with integer speed variables, you need to make sure the speeds are in the range of the integer variables, that means you cannot have an arbitrarily fine raster.

But then, you might also want to describe some very slow objects, like the hour hand of a clock. As this is about 6 orders of magnitude slower than the bullet objects, the first ld(10⁶) ≈ 20 bits are zero, that rules out short int types from the start. Ok, today we have longs everywhere, which leave us with a still-comfortable 12 bits. But even then, the clock speed will be exact to only to four decimal places. That's not a very good clock... but it's certainly ok for a game. Just, you would not want to make the raster much coarser than it already is.

...which leads to problems if some day you should like to introduce a new, even faster type of object. There is no "headroom" left.

What happens if we choose a float type? Same size of 32 bits, but now you have a full 24 bits of precision, for all objects. That means, the clock has enough precision to stay in sync up-to-the-seconds for years. The bullets have no higher precision, but they only "live" for fractions of a second anyway, so it would be utterly useless if they had. And you do not get into any kind of trouble if you want to describe even much faster objects (why not speed of light? No problem) or much slower ones. You certainly won't need such things in a game, but you sometimes do in physics simulations.

And with floating-point numbers, you get this same precision always and without first having to cleverly choose some non-obvious raster. That is perhaps the most important point, as such choice necessities are very error-prone.

5
  • Integers are perfectly accurate. Inaccuracy is dependend from incorrect calculation.
    – fjdumont
    Commented Jun 28, 2011 at 6:37
  • 17
    Integers are perfectly accurate only when you use them to actually represent integer (ℤ) numbers. Representing anything else means, indeed, incorrect calculation. In such a case, you have two possibilities: either define some type that perfectly suits the numbers you actually want to represent. this is possible, for instance Mathematica can do it. But it's very complicated and time-expensive, and usually not worth the effort because you do not actually need perfect precision. But you do need good precision, and that's where floats generally do a better job than integers. Commented Jun 28, 2011 at 10:58
  • "That means, the clock has enough precision to stay in sync up-to-the-seconds for years. " -- the problem with this argument is that while the speed value may have plenty of precision, the "position" value it is repeatedly added to doesn't. Commented Jul 24 at 18:08
  • @PeterGreen but you don't need to know the position with much accuracy if the object is far away from the observer, whereas a slow-moving object will stay in close range for a long time and therefore have good position accuracy. By the time a slow-moving object has moved far away you would also in physical reality not expect good position accuracy (due to battery failure / plate tectonics / solar wind...), but you would never experience overflow wraparound or getting stuck to a fixed raster like you get with integers. Commented Jul 25 at 8:08
  • Of course, all of this assumes you're describing each object's position relative to its relevant neighbours, not to some arbitrary global zero point. Commented Jul 25 at 8:14
57

You use them when you're describing a continuous value rather than a discrete one. It's not any more complicated to describe than that. Just don't make the mistake of assuming any value with a decimal point is continuous. If it changes all at once in chunks, like adding a penny, it's discrete.

0
30

You really have two questions here.

Why does anyone need floating point math, anyway?

As Karl Bielefeldt points out, floating point numbers let you model continuous quantities - and you find those all over the place - not just in the physical world, but even places like business and finance.

I've used floating point math in many, many areas in my programming career: chemistry, working on AutoCAD, and even writing a Monte Carlo simulator to do financial predictions. In fact, there's a guy named David E. Shaw who's used applied floating-point based scientific modeling techniques to Wall Street to make billions.

And, of course, there's computer graphics. I consult on developing eye candy for user interfaces, and trying to do that nowadays without a solid understanding of floating point, trigonometry, calculus, and linear algebra, would be like showing up to a gun fight with a pocketknife.

Why would anyone need a float versus a double?

With IEEE 754 standard representations, a 32-bit float gives you about 7 decimal digits of accuracy, and exponents in the range 10-38 to 1038. A 64-bit double gives you about 15 decimal digits of accuracy, and exponents in the range 10-307 to 10307.

It might seem like a float would be enough for what anybody would reasonably need, but it's not. For instance, many real-world quantities are measured in more than 7 decimal digits.

But more subtly, there's a problem colloquially called "roundoff error". Binary floating point representations are only valid for values whose fractional parts have a denominator that's a power of 2, like 1/2, 1/4, 3/4, etc. To represent other fractions, like 1/10, you "round" the value to the closest binary fraction, but it's a little wrong - that's the "roundoff error". Then when you do math on those inaccurate numbers, the inaccuracies in the results can be far worse than what you started with - sometimes the error percentages multiply, or even pile up exponentially.

Anyway, the more binary digits you have to work with, the closer your rounded-off binary representation will be to the number you're trying to represent, so its roundoff error will be smaller. Then when you do math on it, if you have a lot of digits to work with, you can do a lot more operations before the cumulative roundoff error piles up to where it's a problem.

Actually, 64-bit doubles with their 15 decimal digits aren't good enough for many applications. I was using 80-bit floating point numbers in 1985, and IEEE now defines a 128-bit (16-byte) floating point type, for which I can imagine uses.

1
  • 2
    +1 Bob my experience with high-resolution control systems like telescope for astronomy is that 64bit doubles aren't good enough unless you sort your terms. Ditto for fire control and long range navigation Commented Jun 29, 2011 at 3:46
20

It's a common misconception, that everywhere you're dealing with money, you should store it's value as integer (cents). While in some simple cases like on-line store it's true, if you have something more advanced it doesn't help much.

Let's have example: a developer makes $100,000 a year. What is his exact month's salary? Using integer you get result $8333.33 (¢833333), which multiplied by 12 is $99,999.96. Did keeping it as integer help? No, it didn't.

Do banks always use decimal/integer values? Well, they do for transactional part. But for example as soon as you start talking investment banking, with exception of keeping track of actual transactions, everything else is floats. Since it's all in-house code, you won't see it, but you can take a peak at QuantLib, which is essentially the same (except much cleaner ;-).

Why use floats? Because using decimal doesn't help at all when you're using functions like square root, logarithms, powers with non-integer exponents etc. And of course floats are way faster than Decimal types.

5
  • 2
    @Job - decimals and floats are very different. You can store 0.1 exactly in a decimal type, but not in a float or double. Commented Jun 28, 2011 at 11:58
  • 3
    I had another question. If you paid $100,000/12 and used a float. Why would the result be exactly $100,000? Why wouldnt the float (or decimal) round up or down everytime someone is paid? I am talking about when writing a cheque (you cant do 1/2 or 1/3 a cent) or a direct deposit (i assume it has the same limitations)
    – user2528
    Commented Jun 28, 2011 at 16:39
  • @acid: >>> x = 100000/12.0 >>> x*12 100000.0
    – vartec
    Commented Jun 28, 2011 at 16:44
  • reread my comment? my question is when i use software to create a cheque every month. Since one cannot pay 1/2 a cent how does the person get the full amount after a year?
    – user2528
    Commented Jun 28, 2011 at 18:33
  • 2
    @acid: you can't using straight division, regardless if you use integer, decimal or divide as float then round. That's the whole point, using decimal does not help it that case.
    – vartec
    Commented Jun 29, 2011 at 12:58
4

What you have described are perfectly good work arounds for situations where you control all the inputs and outputs.

In the real word it isn't the case. You'll need to be able to cope with systems that supply you their data as some real value to some degree of precision and will expect you to return the data in the same format. In such cases you will encounter these problems.

In fact you will encounter these problems even if you use the tricks you list. When calculating 17.5% tax on a price you're going to get fractional cents whether you store the value as dollars or cents. You have to get the rounding correct as the tax man gets very upset if you don't pay him enough. Using the correct money types (whatever they are in the language you are using) will save you from a world of pain.

7
  • What is the money type? (language or reference link) and why is that the 'correct' type? Is it because its... 128bits or more something? My other why would using my 'tricks' be incorrect? You have a whole number by cent. If you multiple it by .175 you'll get a whole number and use it for whatever you want. Thinking about your example i think float would be able to hold my value with enough precision but i wont have to worry about 0.3f==0.3d being false. -edit- and +1
    – user2528
    Commented Jun 27, 2011 at 20:35
  • 1
    @acidzombie24 - I didn't mean a specific type, but what ever type your language uses to represent money values. Also if you have 10 cents and multiply by 0.175 you have 1.75 cents - how do you deal with that with integer arithmetic? Is it 1 cent or 2 cents? Get it wrong and your customer could end up owning the tax man a lot of money.
    – ChrisF
    Commented Jun 27, 2011 at 20:36
  • You should never multiply 10 (an integer) by .175 (a real/floating number) because you shouldn't mix exact numbers with inexact numbers; the result will be inexact. In other words, in a system of exact numbers, a value like .175 would never exist, and so this is a non-sensical calculation. A better solution is to multiply 10000 by 175 and manually insert a decimal point where appropriate. Commented Jun 27, 2011 at 20:48
  • 8
    @Barry - I know. I was trying to illustrate the type of problem you get. Also a value like 0.175 does exist if the tax rate is 17.5% and you need to calculate the tax on an item that costs 10 cents.
    – ChrisF
    Commented Jun 27, 2011 at 20:51
  • 1
    @acidzombie: The correct type to use for money is a fixed-point decimal with high (at least 4 decimal points) precision. No ifs, ands, or buts. Storing money values as cents is not sufficient, because in practice it only gives you two points of precision.
    – Aaronaught
    Commented Jun 27, 2011 at 21:37
3

"God created the whole numbers, everything else is Man’s work." – Leopold Kronecker (1886).

By definition, you don't need any other kinds of numbers. Turing completeness for a programming language is based on the simple relationships among the various kinds of numbers. If you can work with whole numbers (a/k/a natural numbers), you can do anything.

The question is kind of specious because you don't need them. Perhaps you want places where it's convenient or optimal or cheaper or something?

3
  • 7
    We can also dispense with whole numbers, since one can also construct them using only set theory operations and the empty set. But both that and arguing from Turing completeness are academic reductionism carried to an extreme.
    – Bob Murphy
    Commented Jun 27, 2011 at 21:59
  • 4
    Also, Turing completeness only applies to computing. Neither whole numbers nor even rationals are mathematically complete, since neither is closed to convergence of Cauchy sequences. So Kronecker was full of hot air: if you want a complete metric space that includes the whole numbers, you have to get real: xkcd.com/849
    – Bob Murphy
    Commented Jun 27, 2011 at 22:04
  • 1
    @Bob Murphy: "academic reductionism carried to an extreme". Precisely. The question is poor and leads to this as possible answer.
    – S.Lott
    Commented Jun 27, 2011 at 22:13
2

The primary advantage of floating-point types is that from a run-time perspective, two or three formats (I wish more languages supported 80-bit formats) will sufficient for the fast majority of computational purposes. If programming languages could easily support a family of fixed-point types, the hardware complexity required for a given level of performance would often be lower with fixed-point types than with floating-point. Unfortunately, providing such support is far from "easy".

For a programming language to satisfy 98% of applications' numerical needs efficiently, it would have to include dozens of types, and provide define operations for what may be hundreds of combinations; further, even if a programming language had wonderful fixed-point support, some applications would still need to maintain roughly-constant relative precision over a sufficiently large range as to require floating-point. Given that floating-point math is going to be necessary on some occasions in any event, having hardware vendors focus on math performance with two or three floating-point formats, and having code use those formats whenever they work reasonably well, will generally achieve better "bang for the buck" than would trying to optimize the behavior of fixed-point math.

Incidentally, fixed-point math was more advantageous with 8-bit and 16-bit processors than with 32-bit ones. On an 8-bit processor, in a situation where 32 bits wouldn't quite suffice, a 40-bit type would only cost 25% more space and 25-50% more time than the 32-bit type, and would require 37.5% less space and 37.5-60% less time than a 64-bit type. On a 32-bit platform, if a 32-bit type won't suffice for something, there's often little reason to use anything less than 64 bits. If a 48-bit fixed-point type would be adequate, a 64-bit "double" will work just as well as would the fixed-point type.

0
1

In a sentence, floating-point decimal types encapsulate the conversion to and from integer values (which is all the computer knows how to deal with at the binary level; there is no decimal point in binary) providing a logical, generally easy-to-understand interface for calculations of decimal numbers.

Frankly, saying that you don't need floats because you know how to do decimal math using integers is like saying you know how to do arithmetic longhand, so why use a calculator? So you know the concept; bravo. Doesn't mean you have to exercise that knowledge all the time. It is often faster, cheaper, and more understandable to a non-binary-whiz to simply say 3.5 + 4.6 = 8.1 rather than converting the sig figs to an integer quantity.

0

Generally, you should be very careful of using them. Understanding the loss of precision that can arise from even simple calculations is a challenge. For example, averaging a list of numbers like this is a very bad idea:

double average(List<Double> data) {
  double ans = 0;
  for(Double d : data) {
    ans += d;
  }
  return ans / data.size();
}

The reason is that, for sufficiently large lists, you basically lose all the data points when ans gets large enough (see e.g. this). The problem with this code is that for small lists, it'll probably just work --- it's only at scale that it breaks.

Personally, I think you should only use them when: a) the calculation really must be fast; b) you don't care that the result is likely to be way off (unless you really know what you're doing).

0

Floats make a lot of sense if you look at them from a measurement/experimental science/engineering background. They are essentially the computerised version of "standard notation" that kids are taught about in science classes. Generally in measurement the magnitude of your errors are proportionate to the magnitude of the value being measured.

Fixed point generally means you need to micromanage your scale factors. If your numbers are too small then your results become very inaccurate. If your numbers are two large they overflow your integer type.

This gets worse if what you are implementing is not a concrete application where you know the physical meaning of the values, but some generic bit of maths functionality that will be used in multiple applications with the application author assigning meaning to the numbers. What should the argument and result types for a fixed-point square or square-root function be? what about an arbitrary power function?

Floating point, in most cases, frees you from needing to micromanage your scale factors. A single precision floating point number gives you about 7 digits of precision, which most of the time is far better than your experimental accuracy. Double precision is good enough for nearly any real-world measurement.

That said there are a number of gotcha's with floats.

  1. Many numbers that are nice and clean in decimal don't have a perfect representation as binary fractions. This is most acute with money, but it can come up in other situations too.
  2. Coordinates can be problematic, because they get less accurate the further you get from the centre of the coordinate system.
  3. Repeated addition of a small value to a large value won't always produce the results you expect. This often comes up with things like movement where a "speed" value is repeatedly added to a "distance" or "position" value.
  4. You can get different results based on compiler optimisations, target architecture and global FPU state. This can be problematic if you need you results to be reproducible.
-1

One thought is that you would use the float or double representations when you need to deal with values outside the integer range.

Today's architectures (roughly) have an signed integer range of +/- 2,147,483,647 (32 bit) or +/- 9,223,372,036,854,775,807 (64 bit). Unsigned extends that by a factor of 2.

IEEE 754 floats (roughly) go from +/- 1.4 × 10^−45 to 3.4 × 10^38. Double extends that range to +/- 5×10−324 ±2.225×10^−308 with lots of conditions and specifics omitted here.

Of course, the most stunningly obvious reason is that you might need to represent -0 ;-)

3
  • Numbers primarily from wikipedia articles and are meant to be illustrative. Except -0, that's just for fun.
    – Stephen
    Commented Jun 27, 2011 at 20:42
  • Problem is there are a LOT of integers in that huge range that are not represented at all. Commented Jun 27, 2011 at 20:50
  • @BarryBrown Absolutely right. "lots of conditions and specifics omitted" though.
    – Stephen
    Commented Jun 27, 2011 at 21:00
-1

The usual reason is because they are fast as the JVM typically uses underlying hardware support (unless you use strictfp).

See https://stackoverflow.com/questions/517915/when-to-use-strictfp-keyword-in-java for what strictfp implies.

2
  • Floating point math is faster than integer math? On what processor do floating point calculations take fewer cycles than integer calculations?
    – this.josh
    Commented Jun 28, 2011 at 7:48
  • 1
    @this.josh, strongly depends on the number of digits you have in your numbers. Also integers cannot divide precisely which may or may not be important.
    – user1249
    Commented Jun 28, 2011 at 8:19
-2

That's why we need 256bit operating systems.

The Plank length (the smallest distance you can measure) = 10^-35m
The observable universe is 14Bn parsecs across = 10^25m
So you can measure anything in units of the Plank length as integers if you have only 200 bits of precision.

2
  • 3
    -1: what if you're simulating things on a scale larger than the observable universe?
    – amara
    Commented Nov 9, 2011 at 16:17
  • 2
    @sparkleshy , thats what FAR pointers are for! Commented Nov 9, 2011 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.