3

I would like to be able to extract the significand and exponent of floating-point numbers in NumPy. Getting the exponent as an integer is fine and ok for the significand. Getting the significand as a bitfield would be even more convienient.

I am aware that Python floats have a hex method; however, I wish to use numpy.float32, numpy arrays, and ufuncs. I am also aware of the numpy view method that allows me to see the float as an integer and thus as a binary string:

>>> import numpy as np

>>> b = bin(np.float32(1.23456789).view(np.int32))
'0b111111100111100000011001010010'

>>> b[-23:] # extract last 23 bits of IEEE 754 binary32 float, is significand
'00111100000011001010010'

Extracting the exponent and sign in this way is not convenient, as leading 0s are dropped by bin. (I could left-pad to 32 bits with 0s though…)

In any case, because bin is not a ufunc, this is not convenient and I would have to iterate over the array.

Isn't there any more convenient approach to doing what I want?

equaeghe
  • 1,644
  • 18
  • 37
  • 1
    Have a look at [this answer](https://stackoverflow.com/a/16444786/3214872), it might point you in the riht direction – GPhilo Sep 07 '17 at 11:00
  • 1
    @GPhilo: Thanks. Actually, that answer seems to make it clear that, no, there is no more convenient approach. – equaeghe Sep 07 '17 at 12:14
  • 2
    Take a look at [`numpy.frexp`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.frexp.html) – Mark Dickinson Sep 07 '17 at 12:42
  • @MarkDickinson: `numpy.frexp` gives *a* decomposition into exponent and *mantissa*, not *the* decomposition into exponent and *significand* encoded in an IEEE 754 float. (The exponent given by `frexp` differs by one from the IEEE 754 float exponent.) – equaeghe Sep 07 '17 at 13:59
  • @equaeghe: Yes, but it's trivial to get from the `frexp` result to the IEEE 754 decomposition. As you say, the exponent differs by one, so subtract one from the exponent and double the significand. – Mark Dickinson Sep 08 '17 at 11:40
  • @MarkDickinson: True, but I'd still need to extract the significand separately. I don't see how I could do all the steps any shorter than in my self-answer. Perhaps you can add an answer for me to up-vote? – equaeghe Sep 08 '17 at 13:35

1 Answers1

4

GPhilio's comment triggered a more thorough search on SO which resulted in the following solution, based on an answer to “extracting mantissa and exponent from double in c#”:

import numpy as np

def decompose(x: np.float32): 
    """decomposes a float32 into negative, exponent, and significand"""
    negative = x < 0
    n = np.abs(x).view(np.int32) # discard sign (MSB now 0),
                                 # view bit string as int32
    exponent = (n >> 23) - 127 # drop significand, correct exponent offset
                               # 23 and 127 are specific to float32
    significand = n & np.int32(2**23 - 1) # second factor provides mask
                                          # to extract significand
    return (negative, exponent, significand)

This approach with bit-level operations of integers is actually more convenient that going to the actual bitstring itself.

equaeghe
  • 1,644
  • 18
  • 37