## 0.05 = 0.04999 . . .

### 0.05 = 0.04999 . . .

Ok so I regularly fill in forms on Adobe, it automatically totals columns except the final one where the last digit is as above.

How very queer.
Get it up ye. Heid the Ba
Enlightened One Tree hugging, veggie, sandal wearing, pinko Euroweasel
Mr. Sexy Ass

Posts: 99425
Joined: Wed Aug 02, 2006 12:20 pm
Location: Edinburgh, Scotland

### Re: 0.05 = 0.04999 . . .

Standard mathematics - yes, 0.05 and 0.049999999999999999999999999999<ad infinitum> are two ways of writing the same number.

This really bothers some people, and on various occasions, I have tried, without success, to get the people who insist that these are two different numbers, to explain how their rules of mathematics work.

So, in standard mathematics, they are the same, in the somewhat fuzzy ill-defined mathematics that exists in some people's brains, but which is never clearly articulated, they might be different.

Now, on to an entirely different topic, how do computers represent numbers?

Computer arithmetic is at best an approximation to abstract mathematics. For one, computers use a finite number of bits to represent numbers. They can therefore represent only finitely many numbers, and there are (in standard mathematics) infinitely many numbers in any interval. For example, between 0.4149217 and 0.4149218, there are infinitely many numbers. The computer cannot represent them all.

There is a whole art to how the theoretically perfect world of abstract mathematics is approximated on computers. We tend to think in decimal. The number 5/2 can be represented perfectly accurately in decimal - it is 2.5. The number 5/3 cannot be represented perfectly accurately in decimal if you have finitely many digits. 1.7, 1.666667, 1.66666666666666667, and 1.66666666666666666666666666666666666666666666666666667 are successively more accurate approximations, but they're still not perfect.

The computer does not think in decimal; it thinks in binary. In binary, 7/2 can be represented perfectly accurately. It is 11.1. 7/5 cannot be represented perfectly accurately; it is 1.011001100110011 - approximately.

Now, some numbers cannot be represented perfectly accurately in decimal or binary - for example, 1/3. So what is 1/3+1/3+1/3? By extremely basic arithmetic, it is 1. On a computer, it is not one, because the computer cannot represent 1/3 perfectly accurately. It produces an answer that is slightly different than one.

One way of dealing with this psychologically unsatisfying phenomenon that computer/calculator manufacturers came upon decades ago is, show your results with fewer decimal places than the number you use to represent the numbers. So if 1/3 is 0.333333333333333333333333 on your computer, so that 1/3+1/3+1/3 is 0.999999999999999999999999, you display the answer with eighteen decimal places rather than twenty four, so your inaccurate answer rounds to one. Which is what the human is expecting.

This reduces the frequency of the strange unsatisfying phenomenon, but does not eliminate it completely. No one has figured out how to eliminate it completely.

So I think you have encountered the fact that computer arithmetic is a finite approximation to the infinitely fine world of actual arithmetic.

This happens to me all the time. If I create a financial spreadsheet, enter numbers which represent dollars and cents (so that 17.95 is 17 dollars and 95 cents), and add up a long column of such numbers, the result is frequently something like 2915.34999999999999999999999. Because decimal numbers represent fractions with powers of 2 or 5 in the denominator perfectly, but binary only does it with powers of 2. 34.95 does not have an exact representation in binary.

Reporting bugs in microsoft software (I've reported two) generally results in no response at all; eventually, the bug may be fixed, and if it is, you never know whether your report had anything to do with it or not. Not sure how Adobe works. You can report it, in which case the response may be, sorry, that's the best we can do, given that computers work in binary. Or you might get the Microsoft response.
Be seeing you! Мастер
Moderator Злой Мудак
Mauerspecht

Posts: 21587
Joined: Tue Aug 02, 2005 2:56 pm
Location: Far from Damascus

### Re: 0.05 = 0.04999 . . .

Ah, thanks. I had never thought of this.
Get it up ye. Heid the Ba
Enlightened One Tree hugging, veggie, sandal wearing, pinko Euroweasel
Mr. Sexy Ass

Posts: 99425
Joined: Wed Aug 02, 2006 12:20 pm
Location: Edinburgh, Scotland

### Re: 0.05 = 0.04999 . . .

Floating point representation was a compromise between accuracy and precision against speed and space. However, as speed and space have improved, we've come up with ways to more accurately represent numbers with high precision. For example, Microsoft implemented what they call a floating decimal point type, which allows for exact decimal representation up to however many decimal numbers can be stored in memory. If you need to make your software fast and responsive, though, especially when performing many operations, this probably isn't the best option. In other words, I don't think Microsoft allows this to be used in their spreadsheet software, as they, of course, know best.

In any case, computers are finite. So, concepts like repeating decimals or irrational numbers like pi either have to be treated specially or approximated, hopefully to a degree specific enough for the user's purposes. MM_Dandy
Moderator King of Obscurity

Posts: 4790
Joined: Thu May 12, 2005 9:02 pm
Location: Canton, SD, USA

### Re: 0.05 = 0.04999 . . .

MM_Dandy wrote:For example, Microsoft implemented what they call a floating decimal point type, which allows for exact decimal representation up to however many decimal numbers can be stored in memory.

I did not know that. I assume Microsoft is not making chips now, so that this is strictly something they have in a software library somewhere?

Do you know how they do it? One thought that occurs to me is to represent all numbers as integers, then include some additional bits to indicate how many decimal places things should be moved over. I once wrote a piece of software with a primitive version of that, representing all numbers as integers, and implicitly assuming they are all divided by one hundred. So that the number "three" was represented as the integer 300. This was financial, representing dollars and cents. But I feel like I could implement something like this very quickly myself if all we need is addition and subtraction, just declare a data type with an integer and a "shift" short integer or some such thing, and write addition and subtraction routines. Seems like a few minutes' work. Multiplication would be quite easy also. Division - OK, that might be a little trickier if you want to retain the accuracy. A lot of other functions, trigonometric, exponential, logarithmic, etc., they're not going to be exact anyway, so you could just convert to normal numbers, perform the operation, then change them back. But if you want some special cases to come out exact (for example, if you want the base ten logarithm of 0.001 to be exactly -3), it might be a little tricky.

I think you would still have issues if you tried to do things like add 17,000,000,000,000,000 and 0.000000000000000142 though.
Be seeing you! Мастер
Moderator Злой Мудак
Mauerspecht

Posts: 21587
Joined: Tue Aug 02, 2005 2:56 pm
Location: Far from Damascus

### Re: 0.05 = 0.04999 . . .

Мастер wrote:
MM_Dandy wrote:For example, Microsoft implemented what they call a floating decimal point type, which allows for exact decimal representation up to however many decimal numbers can be stored in memory.

I did not know that. I assume Microsoft is not making chips now, so that this is strictly something they have in a software library somewhere?

Yes, it is in a library. However, upon referencing the the documentation, it turns out that I (and Stack Overflow) are quite wrong. Instead of a memory-limited representation of the exact digits, it is a 128-bit binary representation divided into a sign bit, 96-bits for the numerical representation, and 28 bits (called a "scaling factor") to specify which portion is the decimal fraction.
Microsoft wrote:The binary representation of a Decimal value consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the 96-bit integer and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28. Therefore, the binary representation of a Decimal value the form, ((-2^96 to 2^96) / 10^(0 to 28)), where -(2^96-1) is equal to MinValue, and 2^96-1 is equal to MaxValue.

Мастер wrote:Do you know how they do it? One thought that occurs to me is to represent all numbers as integers, then include some additional bits to indicate how many decimal places things should be moved over. I once wrote a piece of software with a primitive version of that, representing all numbers as integers, and implicitly assuming they are all divided by one hundred. So that the number "three" was represented as the integer 300. This was financial, representing dollars and cents. But I feel like I could implement something like this very quickly myself if all we need is addition and subtraction, just declare a data type with an integer and a "shift" short integer or some such thing, and write addition and subtraction routines. Seems like a few minutes' work. Multiplication would be quite easy also. Division - OK, that might be a little trickier if you want to retain the accuracy. A lot of other functions, trigonometric, exponential, logarithmic, etc., they're not going to be exact anyway, so you could just convert to normal numbers, perform the operation, then change them back. But if you want some special cases to come out exact (for example, if you want the base ten logarithm of 0.001 to be exactly -3), it might be a little tricky.

As far as the type is concerned, I thought it worked something along those lines as well, and the StackOverflow entry that I referenced implicated as much, but that is definitely not the case according to Microsoft. Since it is a binary representation, however, if your problem deals with numbers that cannot be represented in binary or need to be both very large and very precise, you'd still have to roll your own solution.

Мастер wrote:I think you would still have issues if you tried to do things like add 17,000,000,000,000,000 and 0.000000000000000142 though.

Case in point: using decimal types for this operation will result in an overflow exception. MM_Dandy
Moderator King of Obscurity

Posts: 4790
Joined: Thu May 12, 2005 9:02 pm
Location: Canton, SD, USA

### Re: 0.05 = 0.04999 . . .

MM_Dandy wrote:
Microsoft wrote:The binary representation of a Decimal value consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the 96-bit integer and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28. Therefore, the binary representation of a Decimal value the form, ((-2^96 to 2^96) / 10^(0 to 28)), where -(2^96-1) is equal to MinValue, and 2^96-1 is equal to MaxValue.

MM_Dandy wrote:As far as the type is concerned, I thought it worked something along those lines as well, and the StackOverflow entry that I referenced implicated as much, but that is definitely not the case according to Microsoft. Since it is a binary representation, however, if your problem deals with numbers that cannot be represented in binary or need to be both very large and very precise, you'd still have to roll your own solution.

I think we must be interpreting things differently, because the description from Microsoft sounds to me like it matches my concept. You represent an integer, then have some extra bits indicating how many decimal places you move the decimal point over. The fact that the integer part is represented in binary is not that important - it allows a wider range using the same number of bits than binary coded decimal would. However, integer representations are exact. So if you wanted 1,230.2 (which has an exact decimal representation, but not an exact binary representation), you would have the sign bit set to positive, the binary (and exact) representation of 12,302 in the 96 bit part, and then in the "scaling" part, you indicate that you want to move it over one decimal place. Voila, an exact representation of 1,230.2. Am I interpreting this correctly? After all, if it can't represent decimal fractions exactly, what's the point? We already have real numbers which can be manipulated using processor commands, probably far more efficiently than these numbers can be.

Some things that strike me as odd, given my (possibly severely outdated) knowledge of how computers represent numbers.

(1) It used to be, the sign bit not only indicated the sign, but also indicated how the rest of the number was interpreted. To keep it simple, let us just use 8 bits. Then the number one would be represented by 00000001. The first bit is the sign bit, and "0" means positive. However, negative one would be 11111111, not 10000001. That is, negative one is not just the same as one, but with the sign bit flipped - it is quite different. This made certain things easier, for example, addition worked by manipulating the bits in exactly the same way, whether the numbers were positive or negative. But maybe processors don't work that way anymore? Alternatively, if all arithmetic is done in software, rather than by using processor commands, maybe it's OK if the way this library represents numbers is different than the way the processor does it.

(2) A consequence of the above is that the largest negative number that could be represented was one larger than the largest positive number. In the 8 bit example, the largest positive number would be 01111111, which is 127. However, the largest negative number is 10000000, which is -128. If we take Microsoft's description literally, then the largest possible negative integer is just the negative of the largest possible positive integer (and their description does say this), and the "extra" number present in the 96 bit space gives us a second way to represent zero. (We can have "positive" or "negative" zero.) I guess if you ask the library whether positive zero is equal to negative zero, it would say "yes", even though the bit representations are different?

(3) For the scaling factor, why 28? The largest integer is about 7.92 times 10^28, so maybe that 28 enters into it. But it just seems such a strange number. You can represent 0 through 28 (and in fact 0 through 31) using 5 bits, so why not let it be up to 31? And in this case, one sign bit plus 96 integer bits plus 5 "scaling" bits makes 102 bits, with some invalid representations (e.g., scaling bits could say 29, 30, or 31, but these are beyond the allowable values). So are the other 26 bits wasted? Why not use them to allow more scaling? An enormous range of numbers could be represented this way.

My thoughts. Comments welcome Be seeing you! Мастер
Moderator Злой Мудак
Mauerspecht

Posts: 21587
Joined: Tue Aug 02, 2005 2:56 pm
Location: Far from Damascus 