You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Binary floating-point arithmetic holds many surprises like this. The problem
129
148
with "0.1" is explained in precise detail below, in the "Representation Error"
130
-
section. See `The Perils of Floating Point <https://www.lahey.com/float.htm>`_
149
+
section. See `Examples of Floating Point Problems
150
+
<https://jvns.ca/blog/2023/01/13/examples-of-floating-point-problems/>`_ for
151
+
a pleasant summary of how binary floating point works and the kinds of
152
+
problems commonly encountered in practice. Also see
153
+
`The Perils of Floating Point <https://www.lahey.com/float.htm>`_
131
154
for a more complete account of other common surprises.
132
155
133
156
As that says near the end, "there are no easy answers." Still, don't be unduly
@@ -158,26 +181,34 @@ statistical operations supplied by the SciPy project. See <https://scipy.org>.
158
181
Python provides tools that may help on those rare occasions when you really
159
182
*do* want to know the exact value of a float. The
160
183
:meth:`float.as_integer_ratio` method expresses the value of a float as a
161
-
fraction::
184
+
fraction:
185
+
186
+
.. doctest::
162
187
163
188
>>> x =3.14159
164
189
>>> x.as_integer_ratio()
165
190
(3537115888337719, 1125899906842624)
166
191
167
192
Since the ratio is exact, it can be used to losslessly recreate the
168
-
original value::
193
+
original value:
194
+
195
+
.. doctest::
169
196
170
197
>>> x ==3537115888337719/1125899906842624
171
198
True
172
199
173
200
The :meth:`float.hex` method expresses a float in hexadecimal (base
174
-
16), again giving the exact value stored by your computer::
201
+
16), again giving the exact value stored by your computer:
202
+
203
+
.. doctest::
175
204
176
205
>>> x.hex()
177
206
'0x1.921f9f01b866ep+1'
178
207
179
208
This precise hexadecimal representation can be used to reconstruct
180
-
the float value exactly::
209
+
the float value exactly:
210
+
211
+
.. doctest::
181
212
182
213
>>> x ==float.fromhex('0x1.921f9f01b866ep+1')
183
214
True
@@ -186,17 +217,43 @@ Since the representation is exact, it is useful for reliably porting values
186
217
across different versions of Python (platform independence) and exchanging
187
218
data with other languages that support the same format (such as Java and C99).
188
219
189
-
Another helpful tool is the :func:`math.fsum` function which helps mitigate
190
-
loss-of-precision during summation. It tracks "lost digits" as values are
191
-
added onto a running total. That can make a difference in overall accuracy
192
-
so that the errors do not accumulate to the point where they affect the
193
-
final total:
220
+
Another helpful tool is the :func:`sum` function which helps mitigate
221
+
loss-of-precision during summation. It uses extended precision for
222
+
intermediate rounding steps as values are added onto a running total.
223
+
That can make a difference in overall accuracy so that the errors do not
224
+
accumulate to the point where they affect the final total:
225
+
226
+
.. doctest::
194
227
195
228
>>> 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1==1.0
196
229
False
197
-
>>> math.fsum([0.1] *10) ==1.0
230
+
>>> sum([0.1] *10) ==1.0
198
231
True
199
232
233
+
The :func:`math.fsum()` goes further and tracks all of the "lost digits"
234
+
as values are added onto a running total so that the result has only a
235
+
single rounding. This is slower than :func:`sum` but will be more
236
+
accurate in uncommon cases where large magnitude inputs mostly cancel
0 commit comments