1

I hope this question is not duplicated, since I tried to find an answer already. I've seen many similar questions, but none of them was quite helpful.

I'm writing a program where I need to read matrix values from a file. There are three values for each new line, as usual (row index (int), col index (int), value (float)).

From what I understand, the single precision data type (float) in C can represent values from 1.175494351E-38 to 3.402823466E+38. Knowing this, I cannot understand why my program reads strangely rounded up values from the file, instead of the right one.

The program does this:

int d,c;
float val;
for (int i=0; i<nz; i++) {

  ...

  fscanf(f, "%d %d %f\n", &r, &c, &val);

  ...

}

In the file, I have the line:

1 1 881.41025641026

but the program reads

1 1 881.4102783203125

I also tried to use different format strings to read and print values (such as %g), but the result is the same. When I use double precision the value is correct, so I'm assuming it as something to do with dimension of the data type, but I don't understand exactly how.

lilith
  • 63
  • 5
  • 3
    [1.175494351E-38 ,3.402823466E+38] is the **range** of `float` values, but not all values are representable, there is a rounding. `double` is of greater precision but also not exact. – Jean-Baptiste Yunès Apr 15 '23 at 14:20
  • The are infinitely many real numbers between any two non-equal real numbers, so obviously a floating point type can't represent _all_ of them. That would require infinite memory. Some rounding to representable values must occur. – user17732522 Apr 15 '23 at 14:34
  • `float` has around 7 digits of precision, `double` about 15 digits. – Barmar Apr 15 '23 at 14:47
  • 2
    A single precision float is only guaranteed to accurately represent up to 6 decimal digits; more properly, you can convert from binary to decimal and back again and get the original value *if* the decimal conversion requires 6 digits or less. Basically, you can't trust anything after `881.410`. Double precision guarantees up to 10 decimal digits. There are an infinite number of real values just between 0 and 1, but you can't squeeze an infinite number of values into a finite number of bits. Most real values *cannot* be represented accurately in native floating point types. – John Bode Apr 15 '23 at 15:25
  • @lilith, [32-bit `float`](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) can exactly encode about 2^32 different values. `881.410_25641026` is **not** one of them. `881.410_2783203125` is the exact value of the closest `float`. – chux - Reinstate Monica Apr 15 '23 at 15:38
  • 1
    @lilith, "When I use double precision the value is correct," --> not quite. [64-bit `double`](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) can exactly encode about 2^64 different values. `881.4102564102_6` is **not** one of them. `881.4102564102_5998993427492678165435791015625` is the exact value of the closest `double` or `0x1.b8b4834834854p+9` in hex notation. – chux - Reinstate Monica Apr 15 '23 at 15:43
  • If you are reading decimal values from a file, storing them in a variable of type `float` or `double`, and then printing them back out again in decimal, you have not one but two conversions: one from decimal to binary when you read the file, and one from binary back to decimal when you print the numbers out again. – Steve Summit Apr 15 '23 at 16:01
  • 1
    For a number that's exactly representable in both decimal and binary, like `123.125`, this will work perfectly, with no anomalies. But for most numbers — those that are representable exactly in decimal, but not in binary — the double-conversion process ends up rounding your original number to the nearest number that *can* be exactly represented in binary. That's what's happening with your number `881.41025641026`. – Steve Summit Apr 15 '23 at 16:01
  • 1
    Both `float` and `double` have this issue. The reason you're not seeing the problem with type `double` is probably that `double`'s precision is up around 16 decimal digits, while the default precision of the `printf` formats is 6. If you print using `%.20f` or `%.20g`, you'll see some anomalies with type `double` also. But that's also a good way to solve the problem: when you print your numbers back out, select a precision like `%.3f` or `%.5g` — choosing a precision that's appropriate for your data — and you should get acceptable results. – Steve Summit Apr 15 '23 at 16:05

0 Answers0