Precision in C floats

12,434

Solution 1

"6 digits after the decimal point" is nonesnse, and your example is a good demonstration of this.

This is an exact specification of the float data type.

The precision of the float is 24 bits. There are 23 bits denoting the fraction after the binary point, plus there's also an "implicit leading bit", according to the online source. This gives 24 significant bits in total.

Hence in decimal digits this is approximately:

24 * log(2) / log(10) = 7.22

Solution 2

It sounds like you're asking about precision to decimal places (digits after the decimal point), whereas significant figures (total number of digits excluding leading and traling zeroes) is a better way to describe accuracy of numbers.

You're correct in that the number of digits after the decimal point will change when the number is larger - but if we're talking precision, the number of significant figures will not change when the number is larger. However, the answer isn't simple for decimal numbers:


Most systems these days use IEE floating point format to represent numbers in C. However, if you're on something unusual, it's worth checking. Single precision IEE float numbers are made up of three parts:

  • The sign bit (is this number positive or negative)
  • The (generally also signed) exponent
  • The fraction (the number before the exponent is applied)

As we'd expect, this is all stored in binary.


How many significant figures?

If you are using IEE-754 numbers, "how many significant figures" probably isn't an easy way to think about it, because the precision is measured in binary significant figures rather than decimal. floats have only 23 bits of accuracy for the fraction part, but because there's an implicit leading bit (unless the fraction part is all zeroes, which indicates a final value of 1), there are 24 effective bits of precision.

This means there are 24 significant binary digits, which does not translate to an exact number of decimal significant figures. You can use the formula 24 * log(2) / log(10) to determine that there are 7.225 digits of decimal precision, which isn't a very good answer to your question, since there are numbers of 24 significant binary digits which only have 6 significant decimal digits.

So, single precision floating point numbers have 6-9 significant decimal digits of precision, depending on the number.

Interestingly, you can also use this precision to work out the largest consecutive integer (counting from zero) that you can successfully represent in a single precision float. It is 2^24, or 16,777,216. You can exactly store larger integers, but only if they can be represented in 24 significant binary digits.


Further trivia: The limited size of the fraction component is the same thing that causes this in Javascript:

> console.log(9999999999999999);
10000000000000000

Javascript numbers are always represented as double precision floats, which have 53 bits of precision. This means between 2^53 and 2^54, only even numbers can be represented, because the final bit of any odd number is lost.

Solution 3

The precision of floating point numbers should be measured in binary digits, not decimal digits. This is because computers operate on binary numbers, and a binary fraction can only approximate a decimal fraction.

Language lawyers will say that the exact width of a float is unspecified by the C standard and therefore implementation-dependent, but on any platform you are likely to encounter a C float means an IEEE754 single-precision number.

IEEE754 specifies that a floating point number is in scientific notation: (-1)s×2e×m
where s is one bit wide, e is eight bits wide, and m is twenty three bits wide. Mathematically, m is 24 bits wide because it's always assumed that the top bit is 1.

So, the maximum number of decimal digits that can be approximated with this representation is: log10(224) = 7.22 . That approximates seven significant decimal digits, and an exponent ranging from 2-126 to 2127.

Notice that the exponent is measured separately. This is exactly like if you were using ordinary scientific notation, like "A person weighs 72.3 kilograms = 7.23×104 grams". Notice that there are three significant digits here, representing that the number is only accurate to within 100 grams. But there is also an exponent which is a different number entirely. You can have a very big exponent with very few significant digits, like "the sun weighs 1.99×1033 grams." Big number, few digits.

Solution 4

In a nutshell, a float can store about 7-8 significant decimal digits. Let me illustrate this with an example:

1234567001.00
         ^
         +---------------- this information is lost

.01234567001
           ^ 
           +-------------- this information is lost

Basically, the float stores two values: 1234567 and the position of the decimal point.

Now, this is a simplified example. Floats store binary values instead of decimal values. A 32-bit IEEE 754 float has space for 23 "significant bits" (plus the first one which is always assumed to be 1), which corresponds to roughly 7-8 decimal digits.

 1234567001.00 (dec) =

 1001001100101011111111101011001.00 (bin)  gets rounded to

 1001001100101011111111110000000.00 =
  |    23 bits           |

 1234567040.00 (dec)

And this is exactly what C produces:

void main() {
    float a = 1234567001;
    printf("%f", a);      // outputs 1234567040
}
Share:
12,434
iceman_w
Author by

iceman_w

Updated on June 04, 2022

Comments

  • iceman_w
    iceman_w almost 2 years

    Generally we say that a float has precision of 6 digits after the decimal point. But if we store a large number of the order of 10^30 we won't get 6 digits after the decimal point. So is it correct to say that floats have a precision of 6 digits after the decimal point?

  • Crashworks
    Crashworks about 12 years
    Note to language pedants: there are exotic platforms where a C float is not an IEEE754 single, but if the OP were working on them, OP would know the answer to this question already. Niggling about IBM/370s here would be unnecessarily confusing.
  • iceman_w
    iceman_w about 12 years
    Thanks. This resolves an argument I had with a TA in my CS lab.
  • Timothy Jones
    Timothy Jones about 12 years
    The calculation is slightly incorrect as there are 24 effective bits of precision (see your link). I'll give you an upvote once you've corrected it :)
  • valdo
    valdo about 12 years
    @Timothy Jones: oops, you're right. I've read the article inattentionally
  • phoog
    phoog about 12 years
    More language pedantry: "binary digit" is oxymoronic, since "digit" comes from the latin word for "ten"; that's one reason that the word "bit" was invented to replace the phrase "binary digit". Niggling about this here would be counterproductive, though, since it's necessary to emphasize the "binary digit" sense of "bit".
  • Sam
    Sam almost 12 years
    This is all provided your decimal floating point value can be represented exactly by the finite precision binary floating point number.
  • Sam
    Sam almost 12 years
    @Crashworks: exotic? Try a VAX or Alpha, very much alive today at my company. Lots of stuff still running (huge pain in the neck to maintain cross platform code).
  • Pascal Cuoq
    Pascal Cuoq over 10 years
    @phoog A new user without the reputation for writing comments wrote: your "more language pedantry" is unfortunately not correct: digit is not from Latin Ten: its root is lating digitus, which means finger. Representing numbers with hands, the metaphoric use for a "figure" is absolutely correct. You probabily mean decimal, which has the latin root "decem" which is ten.