0

I am playing around with plain old C. I am simply calculating a number of pennies and then increasing the amount exponentially over a certain number of iterations. At the end of the process, I have large number of cents/pennies that I want to display on the screen as dollars and cents.

My output current looks like this:

"You have 805306365 pennies"

I attempt to convert this number into dollars by defining a new variable as a float:

float totalD = total / 100.00;

The output shows 8053063.50. Why is the division of this int causing a loss of 15 cents? I am trying to display it through a "printf" if that helps:

printf("You have %.2f dollars", totalD);

I know I can just convert my cents into a string and try to format it however I like, but I am perplexed as to why a float would do this. Can anyone give me any insight on why this happens and maybe how to deal with it?

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 10
    If you're using `float`, it will not have enough precision to hold `805306365`. And don't use floating-point types for money. – Mysticial Apr 08 '13 at 02:44
  • 3
    [Read this carefully](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). It will take awhile but it will forever change any ideas you may have about floating pointer numbers. – WhozCraig Apr 08 '13 at 02:59
  • 1
    Don't do math on floating point numbers when integers will suffice. – Carl Norum Apr 08 '13 at 03:00
  • @WhozCraig - if I could put five "helpful" flags on your comment, I would. – Floris Apr 08 '13 at 03:16

3 Answers3

5

updated for accuracy and completeness

You are encountering is a (rather common) problem of precision in floating point numbers. On many compilers, floating point numbers are only 32 bits long, and they use a certain number of those bits (23) for their "significand" (sometimes referred to as "mantissa"), 1 sign bit, and 8 bits for the "exponent" (in a number like 1.23E45, the "1.23" is the significand, and the "45" is the exponent. In binary, you actually have relatively few (24) ones and zeros available so you run out of precision at around digit 6 or 7 in decimal notation).

To illustrate this loss of precision, I wrote a few lines of code:

#include <stdio.h>

int main(){
  float fpennies;
  long lpennies, ii;

  lpennies = 805306360;
  for(ii = 0; ii< 100; ii++) {
      fpennies = lpennies + ii;
      printf("%ld pennies converted to float: %.0f fpennies\n",ii+ lpennies, fpennies);
  }
}

This produced many lines of the type

805306360 pennies converted to float: 805306368 fpennies 
805306361 pennies converted to float: 805306368 fpennies 
805306362 pennies converted to float: 805306368 fpennies 
... 
805306400 pennies converted to float: 805306368 fpennies 
805306401 pennies converted to float: 805306432 fpennies

As you can see, right around 805306400, incrementing the long by just one increments the float representation of the number by 64! This is best explained by looking at the binary representation of the floating point number a little closer.

First, here is the organization of a 32 bit floating point number (from http://upload.wikimedia.org/wikipedia/commons/d/d2/Float_example.svg):

enter image description here

We can get the hex representation of a number with some explicit casting:

printf("%.0f %08x", fpennies, *(unsigned int*)(&fpennies));

For the two values that spanned the jump we saw earlier, this results in

805306368 4e400000
805306432 4e400001

As you can see, the "least significant bit" of the significand increased by 1, but the exponent implies a multiplier of 64. Why 64? Well, let's expand the top few bits:

0x4e40 = 0100 1110 0100 0000 in binary

Since the top bit is the sign bit (0 = positive), and the next eight bits are the exponent, this makes the exponent

1001 1100 = 0x9c = 156

Now the rule for getting from the bits in the floating point to its value (see http://en.wikipedia.org/wiki/Single-precision_floating-point_format ) is

value = (-1)^(sign bit) * (1 + sum(i=1 to 23, bit(23-i)*2^(-i))) * 2^(exponent - 127)

In this case, an change of 1 in the least significant bit (bit 0) adds 2^(-23) * 2^( 156 - 127 ) = 2^6 = 64

Thus, for numbers of this magnitude, the smallest step that can be represented is 64, as you see in the output.

If you want to get around this problem, you can do the thing suggested in Vaughn's answer - work with long integers representing pennies, and using integer math (division, modulo) to obtain the "whole dollars, whole cents" amount.

long int dollars, cents, pennies;
...
dollars = pennies / 100;
cents = pennies % 100;

In this way you can represent some pretty big sums of money without loss of precision.

In practice, when you write

float pennies = 805306365;
printf("you have %f pennies\n", pennies);

You get

You have 805306368 pennies

If you use a double type you will have better luck (in this case).

Floris
  • 45,857
  • 6
  • 70
  • 122
2

I think you would have better luck doing it this way:

printf("You have %d.%02d dollars", total/100,total%100);
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
-1

Consider using long int, or long long to represent pennies. Floats, or doubles are not suitable for what you are trying to do. I suggest reading this discussion on StackOverflow.

Community
  • 1
  • 1
Dave Newman
  • 1,018
  • 10
  • 15