-2

When I cast the numbers in the matrix as np.float32 the values are slightly modified:

In [1]: matrix_32 = np.asarray([
   ...:    [0.7, 3],
   ...:    [1.7, 2],
   ...:    [0.7, 9]
   ...: ], dtype=np.float32)

In [2]: matrix_32
Out[2]: 
array([[ 0.69999999,  3.        ],
       [ 1.70000005,  2.        ],
       [ 0.69999999,  9.        ]], dtype=float32)

However, when I cast the numbers as np.float64 the values are shown as expected:

In [3]: matrix_64 = np.asarray([
   ...:    [0.7, 3],
   ...:    [1.7, 2],
   ...:    [0.7, 9]
   ...: ], dtype=np.float64)

In [4]: matrix_64
Out[4]: 
array([[ 0.7,  3. ],
       [ 1.7,  2. ],
       [ 0.7,  9. ]])

Can somebody explain why this happens?

Alex
  • 3,958
  • 4
  • 17
  • 24
  • 1
    I suggest you read some introduction to Floating Point arithmetic e.g. https://en.wikipedia.org/wiki/Floating-point_arithmetic – Grzegorz Oledzki Nov 24 '17 at 09:19
  • The question is not solely on the introduced inaccuracy, but also on the fact that `np.float32` and `np.float64` appear to have different `__repr__` implementations for their objects. – cs95 Nov 24 '17 at 09:26
  • 1
    I know we get a great many floating-point questions that are easily answered by knowledge of some basic floating-point properties, but marking questions as duplicates is supposed to mean they are exact duplicates. It should not be used to deflect questions in a broad category without providing answers. This question is not a duplicate of [the purported original](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Eric Postpischil Nov 24 '17 at 11:53

1 Answers1

4

When decimal numbers are converted to binary floating-point, they have to be rounded to binary fractions instead of decimal. This rounding changes the values. For float32, the change is large enough to see in the output formatting Numpy uses. For float64, the change is too small to see.

The Numpy samples you show appear to use eight decimal digits or nine significant digits. For normal float32 values, the changes caused by conversion from decimal tend to be around .00000003 times the value being converted (and as large as twice that), so they are visible in nine significant digits. For normal float64 values, the changes caused by conversion from decimal tend to be around .0000000000000001 (10−16) times the value, so they are much too small to be visible in nine significant digits.

For normal float32 values, the change may be as large as 1 part in 16,777,216. For normal float64 values, the change may be as large as 1 part in 9,007,199,254,740,992. That is just for the initial conversion from decimal to binary-floating point. There may be additional roundings when you do arithmetic (exact mathematical results are rounded to values that fit in the floating-point format) and when the number is converted back to decimal for display. (Outside of the normal range, very large numbers may overflow to infinity [so the change is infinite], and very small numbers are rounded more crudely [even to zero for the smallest numbers, so the change is up to 100%].)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    For the record, it's NumPy's output that's relevant here rather than Python's. NumPy has its own `float32` and `float64` types, with their own string representations, independent of those for Python's built-in `float` type. When printing arrays, NumPy rounds to a fixed precision; I think it's 8 significant digits by default (but may be misremembering). – Mark Dickinson Nov 24 '17 at 13:15