classification
Title: testFsum failure caused by constant folding of a float expression
Type: behavior Stage: resolved
Components: Tests Versions: Python 3.9
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: BTaskaya, lemburg, mark.dickinson, pablogsal, rhettinger, stutzbach, tim.peters, vstinner, xdegaye
Priority: normal Keywords: patch

Created on 2019-12-07 11:32 by xdegaye, last changed 2019-12-09 18:22 by mark.dickinson. This issue is now closed.

Files
File name Uploaded Description Edit
foo.x86_64 xdegaye, 2019-12-07 11:32
foo.arm64 xdegaye, 2019-12-07 11:34
Pull Requests
URL Status Linked Edit
PR 17513 merged mark.dickinson, 2019-12-08 21:03
PR 17530 merged miss-islington, 2019-12-09 14:36
Messages (8)
msg357969 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2019-12-07 11:32
Title: testFsum failure caused by constant folding of a float expression

Description:
------------
Python (Python 3.9.0a1+ heads/master-dirty:ea9835c5d1) is built on a Linux x86_64. This native interpreter is used to cross-compile Python (using the same source) to Android API 24. Next the installation is done locally to DESTDIR by running 'make install' with the env var DESTDIR set and the standard library modules are compiled by the native interpreter during this process.  The content of DESTDIR is then copied to an arm64 android device (Huawei FWIW). The test_math.MathTests.testFsum test fails on the android device with:

AssertionError: -4.309103330548428e+214 != -1.0

This occurs when testing '([1.7**(i+1)-1.7**i for i in range(1000)] + [-1.7**1000], -1.0)' in test_values.

Next the test_math.py file is touched on the android device to force recompilation of the module and testFsum becomes surprisingly successful.

Investigation:
--------------
The hexadecimal representation of 1.7**n on x86_64 and arm64 are:
* different for n in (10, 100, 1000)
* equal for n in [0, 9] or 11

on x86_64:
>>> 1.7**10
201.59939004489993
>>> (1.7**10).hex()
'0x1.9332e34080c95p+7'

on arm64:
>>> 1.7**10
201.59939004489996
>>> (1.7**10).hex()
'0x1.9332e34080c96p+7'

The output of the following foo.py module that has been run on x86_64 and arm64 are attached to this issue:

#######################
import math, dis

def test_fsum():
    x = [1.7**(i+1)-1.7**i for i in range(10)] + [-1.7**10]
    return x

y = test_fsum()
print(y)
print(math.fsum(y))
dis.dis(test_fsum)
#######################

The only difference between both dissasembly of test_fsum() is at bytecode 16 that loads the folded constant 1.7**10.

Conclusion:
-----------
The compilation of the expression '[1.7**(i+1)-1.7**i for i in range(1000)] + [-1.7**1000]' on x86_64 folds '1.7**1000' to 2.8113918290273277e+230 When the list comprehension (the first term of the expression) is executed on arm64, then 1.7**1000 is evaluated as 2.8113918290273273e+230.  On arm64 1.7**1000 - 2.8113918290273277e+230 = -4.309103330548428e+214, hence the AssertionError above.

This is confirmed by changing testFsum to prevent constant folding by replacing 1000 in the testFsum expression with a variable whose value is 1000.  In that case the test_math module compiled on x86_64 is successful on arm64. This could be a fix for this issue unless this fix would be hiding another problem such as .pyc files portability across different platforms and my knowledge of IEEE 754 is too superficial to answer that point.
msg358024 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-08 20:28
So if I'm understanding correctly, the cause of the issue is that the value `1.7**(i+1)` computed in the last iteration (i=999) of the list comprehension doesn't exactly match the `-1.7**1000` value, because the former is computed at runtime using the libm's pow, while the latter is constant-folded and likely uses something more accurate than `pow`.

I think it should be easy to rewrite the test so that it precomputes the powers of `1.7`, and then makes sure to use those computed values (i.e., so that we're only computing `1.7**1000` once rather than twice, eliminating the possibility of getting different results).
msg358025 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-08 20:30
Note that the exact values of `1.7**i` don't matter here - some sloppiness in the `pow` results should not cause a test failure. The key point is that under fairly mild assumptions about IEEE 754 conformance, the subtractions `1.7**(i+1) - 1.7**i` are always performed exactly, thanks to Sterbenz's lemma.
msg358031 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-08 21:04
@xdegaye Please could you test whether the PR GH-17513 fixes the issue for you?
msg358069 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2019-12-09 11:15
Yes PR GH-17513 does fix the problem.
Thanks Mark.
msg358103 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-09 14:36
New changeset bba873e633f0f1e88ea12fb935cbd58faa77f976 by Mark Dickinson in branch 'master':
bpo-38992: avoid fsum test failure from constant-folding (GH-17513)
https://github.com/python/cpython/commit/bba873e633f0f1e88ea12fb935cbd58faa77f976
msg358120 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-09 17:32
New changeset 3c5feaffde1944052830c896ae39c54e76a2e063 by Mark Dickinson (Miss Islington (bot)) in branch '3.8':
bpo-38992: avoid fsum test failure from constant-folding (GH-17513) (GH-17530)
https://github.com/python/cpython/commit/3c5feaffde1944052830c896ae39c54e76a2e063
msg358123 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-09 18:22
Fixed in master and 3.8. Not sure this is worth backporting to 3.7.
History
Date User Action Args
2019-12-09 18:22:49mark.dickinsonsetstatus: open -> closed
resolution: fixed
messages: + msg358123

stage: patch review -> resolved
2019-12-09 17:32:40mark.dickinsonsetmessages: + msg358120
2019-12-09 14:36:46miss-islingtonsetpull_requests: + pull_request17008
2019-12-09 14:36:38mark.dickinsonsetmessages: + msg358103
2019-12-09 11:15:26xdegayesetmessages: + msg358069
2019-12-08 21:04:36mark.dickinsonsetmessages: + msg358031
2019-12-08 21:03:53mark.dickinsonsetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request16990
2019-12-08 20:30:19mark.dickinsonsetmessages: + msg358025
2019-12-08 20:28:01mark.dickinsonsetmessages: + msg358024
2019-12-08 16:02:29pablogsalsetnosy: + pablogsal
2019-12-07 12:45:27serhiy.storchakasetnosy: + lemburg, rhettinger, mark.dickinson, stutzbach
2019-12-07 11:57:12BTaskayasetnosy: + BTaskaya
2019-12-07 11:34:20xdegayesetfiles: + foo.arm64
2019-12-07 11:32:08xdegayecreate