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
{{ message }}
Repository navigation
Fused multiply-add: proposal to add math.fma() #73468
Fused multiply-add (henceforth FMA) is an operation which calculates the product of two numbers and then the sum of the product and a third number with just one floating-point rounding. More concretely:
r=x*y+z
The value of r is the same as if the RHS was calculated with infinite precision and the rounded to a 32-bit single-precision or 64-bit double-precision floating-point number [1].
Even though one FMA CPU instruction might be calculated faster than the two separate instructions for multiply and add, its main advantage comes from the increased precision of numerical computations that involve the accumulation of products. Examples which benefit from using FMA are: dot product [2], compensated arithmetic [3], polynomial evaluation [4], matrix multiplication, Newton's method and many more [5].
C99 includes [6] fma function to math.h and emulates the calculation if the FMA instruction is not present on the host CPU [7]. PEP-7 states that "Python versions greater than or equal to 3.6 use C89 with several select C99 features" and that "Future C99 features may be added to this list in the future depending on compiler support" [8].
This proposal is then about adding new fma function with the following signature to math module:
math.fma(x, y, z)
'''Return a float representing the result of the operation `x*y + z` with single rounding error, as defined by the platform C library. The result is the same as if the operation was carried with infinite precision and rounded to a floating-point number.'''
Attached is a simple module for Python 3 demonstrating the fused multiply-add operation. On my machine, example.py prints:
[1] https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation
[2] S. Graillat, P. Langlois, N. Louvet. Accurate dot products with FMA. 2006
[3] S. Graillat, Accurate Floating Point Product and Exponentiation. 2007.
[4] S. Graillat, P. Langlois, N. Louvet. Improving the compensated Horner scheme with a Fused Multiply and Add. 2006
[5] J.-M. Muller, N. Brisebarre, F. de Dinechin, C.-P. Jeannerod, V. Lefèvre, G. Melquiond, N. Revol, D. Stehlé, S. Torres. Handbook of Floating-Point Arithmetic. 2010. Chapter 5
[6] ISO/IEC 9899:TC3, "7.12.13.1 The fma functions", Committee Draft - Septermber 7, 2007
[7] https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c
[8] https://www.python.org/dev/peps/pep-0007/
What's the point of adding this in the math module rather than a more specialized library like Numpy?
jurajsukop commented on Jan 16, 2017
jurajsukopmannequin
MannequinAuthor
More actions
I would say because it has wide applicability, especially considering the amount of code it adds. It is similar in spirit to copysign or fsum which are already there and C99 includes it anyway. For simpler things like dot product or polynomial evaluation importing Numpy might be too much of an dependency.
The performance argument unlikely is applicable in this case. I suppose that an overhead of function call in Python make two operators faster than one function call.
Alternatives to fma() for exact computations are integer arithmetic (if all values can be scaled to integers), fractions and decimal numbers.
But since fma() is a part of C (C99), C++ (C++11) and POSIX (POSIX.1-2001) standards for long time, I don't have objections against including it in the math module.
The performance argument unlikely is applicable in this case.
Agreed. This is mainly about accuracy, not speed: the FMA operation is a fundamental building block for floating-point arithmetic, is useful in some numerical algorithms, and essential in others (especially when doing things like double-double arithmetic). It would be valuable to have when prototyping numerical algorithms in pure Python.
Given that it's supported in C99 and on current Windows, I'm +1 on including it in the math module.
Note that implementing this it not quite as straightforward as simply wrapping the libm version, since we'll also want the correct exceptional behaviour, for consistency with the rest of the math module: i.e., we should be raising ValueError where the fma operation would signal the invalid FPE, and OverflowError where the fma operation would signal the overflow FPE.
An implementation note: IEEE 754-2008 leaves it to the implementation to decide whether FMA operations like:
0 * inf + nan
and
inf * 0 + nan
(where nan represents a quiet NaN and the inf and 0 can have arbitrary signs) signal the invalid operation FPE or not. (Ref: 7.2(c) in IEEE 754-2008.)
I'd suggest that in this case we follow what Intel does in its x64 chips with FMA3 support.(according to ). If I'm reading the table in section 2.3 of the Intel Advanced Vector Extensions Programming Reference correctly, Intel does *not* signal the invalid operation FPE in this case. That is, we're following the usual rule of quiet NaN in => quiet NaN out with no exception. This does unfortunately conflict with the IBM decimal specification and Python's decimal module, where these operations *do* set the "invalid" flag (see the spec, and test fmax0809 in the decimal test set).
Isn't the behaviour of quiet NaNs kindof implementation-dependent already?
Not as far as IEEE 754-2008 is concerned, and not as far as Python's math module is concerned, either: handling of special cases is, as far as I know, both consistent across platforms and compliant with IEEE 754. That's not true across Python as a whole, but it should be true for the math module.
If you find an exception to the above statement, please do open a bug report!
I believe there is also one available from netlib, but I don't have that link handy. Arm typically has algorithms available on its repository, but doesn't provide one for fma because all Arm64 based hardware provide an fma instruction natively: https://github.com/ARM-software/optimized-routines
Yes, if you want the 64-bit (double) versions they exist in neighboring files. Notably doing something like (float)fma((double)x, (double)y, (double)z) may not be sufficient in all cases if you want a 32-bit (float) result. There is a subset of operations for which the rounding twice is known to be a non-issue, but I don't recall off the top of my head if fma is included in that, so it may be desirable to have both (and there may even be a performance difference).
Activity
jurajsukop commentedon Jan 16, 2017
Fused multiply-add (henceforth FMA) is an operation which calculates the product of two numbers and then the sum of the product and a third number with just one floating-point rounding. More concretely:
The value of
ris the same as if the RHS was calculated with infinite precision and the rounded to a 32-bit single-precision or 64-bit double-precision floating-point number [1].Even though one FMA CPU instruction might be calculated faster than the two separate instructions for multiply and add, its main advantage comes from the increased precision of numerical computations that involve the accumulation of products. Examples which benefit from using FMA are: dot product [2], compensated arithmetic [3], polynomial evaluation [4], matrix multiplication, Newton's method and many more [5].
C99 includes [6]
fmafunction tomath.hand emulates the calculation if the FMA instruction is not present on the host CPU [7]. PEP-7 states that "Python versions greater than or equal to 3.6 use C89 with several select C99 features" and that "Future C99 features may be added to this list in the future depending on compiler support" [8].This proposal is then about adding new
fmafunction with the following signature tomathmodule:Attached is a simple module for Python 3 demonstrating the fused multiply-add operation. On my machine,
example.pyprints:[1] https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation
[2] S. Graillat, P. Langlois, N. Louvet. Accurate dot products with FMA. 2006
[3] S. Graillat, Accurate Floating Point Product and Exponentiation. 2007.
[4] S. Graillat, P. Langlois, N. Louvet. Improving the compensated Horner scheme with a Fused Multiply and Add. 2006
[5] J.-M. Muller, N. Brisebarre, F. de Dinechin, C.-P. Jeannerod, V. Lefèvre, G. Melquiond, N. Revol, D. Stehlé, S. Torres. Handbook of Floating-Point Arithmetic. 2010. Chapter 5
[6] ISO/IEC 9899:TC3, "7.12.13.1 The fma functions", Committee Draft - Septermber 7, 2007
[7] https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c
[8] https://www.python.org/dev/peps/pep-0007/
vstinner commentedon Jan 16, 2017
Thread on python-ideas:
https://mail.python.org/pipermail/python-ideas/2017-January/044300.html
pitrou commentedon Jan 16, 2017
What's the point of adding this in the math module rather than a more specialized library like Numpy?
jurajsukop commentedon Jan 16, 2017
I would say because it has wide applicability, especially considering the amount of code it adds. It is similar in spirit to
copysignorfsumwhich are already there and C99 includes it anyway. For simpler things like dot product or polynomial evaluation importing Numpy might be too much of an dependency.pitrou commentedon Jan 16, 2017
I don't know. If I want to compute a dot product, the first thing I'll do is import Numpy and then use the
@operator on Numpy arrays.serhiy-storchaka commentedon Jan 16, 2017
The performance argument unlikely is applicable in this case. I suppose that an overhead of function call in Python make two operators faster than one function call.
Alternatives to fma() for exact computations are integer arithmetic (if all values can be scaled to integers), fractions and decimal numbers.
But since fma() is a part of C (C99), C++ (C++11) and POSIX (POSIX.1-2001) standards for long time, I don't have objections against including it in the math module.
mdickinson commentedon Jan 16, 2017
Agreed. This is mainly about accuracy, not speed: the FMA operation is a fundamental building block for floating-point arithmetic, is useful in some numerical algorithms, and essential in others (especially when doing things like double-double arithmetic). It would be valuable to have when prototyping numerical algorithms in pure Python.
Given that it's supported in C99 and on current Windows, I'm +1 on including it in the math module.
Note that implementing this it not quite as straightforward as simply wrapping the libm version, since we'll also want the correct exceptional behaviour, for consistency with the rest of the math module: i.e., we should be raising ValueError where the fma operation would signal the invalid FPE, and OverflowError where the fma operation would signal the overflow FPE.
mdickinson commentedon Jan 16, 2017
An implementation note: IEEE 754-2008 leaves it to the implementation to decide whether FMA operations like:
and
(where nan represents a quiet NaN and the inf and 0 can have arbitrary signs) signal the invalid operation FPE or not. (Ref: 7.2(c) in IEEE 754-2008.)
I'd suggest that in this case we follow what Intel does in its x64 chips with FMA3 support.(according to ). If I'm reading the table in section 2.3 of the Intel Advanced Vector Extensions Programming Reference correctly, Intel does *not* signal the invalid operation FPE in this case. That is, we're following the usual rule of quiet NaN in => quiet NaN out with no exception. This does unfortunately conflict with the IBM decimal specification and Python's decimal module, where these operations *do* set the "invalid" flag (see the spec, and test fmax0809 in the decimal test set).
pitrou commentedon Jan 16, 2017
Isn't the behaviour of quiet NaNs kindof implementation-dependent already?
mdickinson commentedon Jan 16, 2017
Not as far as IEEE 754-2008 is concerned, and not as far as Python's math module is concerned, either: handling of special cases is, as far as I know, both consistent across platforms and compliant with IEEE 754. That's not true across Python as a whole, but it should be true for the math module.
If you find an exception to the above statement, please do open a bug report!
mdickinson commentedon Jan 16, 2017
Here's a pure Python reference implementation, with tests.
mdickinson commentedon Jan 17, 2017
And here's a patch.
27 remaining items
clairvoyante commentedon Feb 21, 2024
FYI :
dotnet/runtime#98704
tannergooding commentedon Feb 22, 2024
It's worth noting that if you want a performant and open source implementation, you have:
I believe there is also one available from netlib, but I don't have that link handy. Arm typically has algorithms available on its repository, but doesn't provide one for
fmabecause all Arm64 based hardware provide anfmainstruction natively: https://github.com/ARM-software/optimized-routinesclairvoyante commentedon Feb 23, 2024
@tannergooding double(64bits float) version is desired, maybe :
https://github.com/amd/aocl-libm-ose/blob/master/src/isa/avx/fma.c
https://github.com/ucb-bar/berkeley-softfloat-3/blob/master/source/s_mulAddF64.c
tannergooding commentedon Feb 23, 2024
Yes, if you want the 64-bit (
double) versions they exist in neighboring files. Notably doing something like(float)fma((double)x, (double)y, (double)z)may not be sufficient in all cases if you want a 32-bit (float) result. There is a subset of operations for which the rounding twice is known to be a non-issue, but I don't recall off the top of my head iffmais included in that, so it may be desirable to have both (and there may even be a performance difference).pythongh-73468: Add math.fma() function
vstinner commentedon Mar 13, 2024
My previous attempt in 2020 failed. I try again in 2024: PR #116667
pythongh-73468: Add math.fma() function
pythongh-73468: Add math.fma() function
pythongh-73468: Add math.fma() function
gh-73468: Add math.fma() function (#116667)
vstinner commentedon Mar 17, 2024
Function added by commit 8e3c953. I only took 7 years only Linux, macOS and Windows get a good support of fma() :-)
pythongh-73468: Add math.fma() function (python#116667)
pythongh-73468: Add math.fma() function (python#116667)
pythongh-73468: Add math.fma() function (python#116667)