2017-01-27 3 views
1

Wie auf dieser Webiste gesehen: https://www.h-schmidt.net/FloatConverter/IEEE754.htmlPython Floats auf 32 Bit begrenzte Anzahl

Ich habe durch fünf oder so zum Thema Stapelüberlauf aussieht, aber sie sind nicht ganz das, was ich suche. Zum Beispiel:

import struct 

getBin = lambda x: x > 0 and str(bin(x))[2:] or "-" + str(bin(x))[3:] 

def floatToBinary64(value): 
    val = struct.unpack('Q', struct.pack('d', value))[0] 
    return getBin(val) 

def binaryToFloat(value): 
    hx = hex(int(value, 2)) 
    return struct.unpack("d", struct.pack("q", int(hx, 16)))[0] 

# floats are represented by IEEE 754 floating-point format which are 
# 64 bits long (not 32 bits) 

# float to binary 
binstr = floatToBinary64(NUMBER) 
print('Binary equivalent of .1:') 
print(binstr + '\n') 

# binary to float 
fl = binaryToFloat(binstr) 
print('Decimal equivalent of ' + binstr) 
print(fl) 

Ich weiß, dass dies ziemlich nah an dem ist, was ich suche. Wenn Sie .1 in diesen Code schreiben, wo Nummer ist, listet es als .1 auf, während es auf der Website gibt es mir: 0.10000000149011612.

Wenn jemand helfen kann, wäre das toll!

+0

Welche Website gibt Ihnen diese Antwort? EDIT: Entschuldigung, es ist ganz oben ... duh – BretD

+0

Warum sagt der Titel 32? –

+0

Ich bin immer noch verwirrt über die Frage ... Also die Website sagt 32-Bit-Floats, Python verwendet 64-Bit-Floats, und Sie sind verwirrt, warum die Werte unterschiedlich sind? – BretD

Antwort

2

Das ist nur, weil Python Ihnen die schönste Darstellung dieser Nummer zeigt. Der Wert ist eigentlich genau das:

>>> '%.60f' % fl 
'0.100000000000000005551115123125782702118158340454101562500000' 

das ist genau das, was die wörtliche 0.1 verwandelt sich in:

>>> '%.60f' % 0.1 
'0.100000000000000005551115123125782702118158340454101562500000' 

(Oh, und es ist nicht 0,10000000149011612, weil das mit 32 anstelle von 64 Bit getan Und eigentlich, dass. eine sollte 0.100000001490116119384765625 sein, diese Konvertierungsseite ist ungenau.)

+0

Oh, das macht mehr Sinn! Obwohl, auf der Website .1 ist 0.10000000149011612 nicht 0.10000000000000000555 irgendwelche Ideen, warum das ist? –

+0

@AndersonP Siehe meinen Kommentar am Ende oder BretDs Kommentar oben. –

+0

Gibt es überhaupt 32 Bit in Python zu simulieren oder kann es nur 64 verwenden? Vielen Dank für Ihre Hilfe –

1

Sie können die Bibliothek gmpy2 verwenden, um mit beliebigen Binärzahlen arbeiten, einschließlich der Standard-32-Bit und 64-Bit-IEEE für Matten. Hier

ein Beispiel:

>>> import gmpy2 
>>> gmpy2.set_context(gmpy2.ieee(64)) 
>>> gmpy2.mpfr("0.1").__format__(".60f") 
'0.100000000000000005551115123125782702118158340454101562500000' 
>>> gmpy2.set_context(gmpy2.ieee(32)) 
>>> gmpy2.mpfr("0.1").__format__(".60f") 
'0.100000001490116119384765625000000000000000000000000000000000' 

Edit: Hinzugefügt Beispiel Funktion mpfr auf 32-Bit-IEEE-Format zu konvertieren.

import gmpy2 
gmpy2.set_context(gmpy2.ieee(32)) 

def mpfr_to_float(x): 
    '''Convert an mpfr object created by the IEEE 32-bit compatible 
    context to a 32 character string containing 0 and 1.''' 

    # Check for special values first. 

    if gmpy2.is_infinite(x): 
     if gmpy2.is_signed(x): 
      return "1" * 9 + "0" * 23 
     else: 
      return "0" + "1" * 8 + "0" * 23 

    if gmpy2.is_nan(x): 
     return "0" + "1" * 31 

    if gmpy2.is_zero(x): 
     if gmpy2.is_signed(x): 
      return "1" + "0" * 31 
     else: 
      return "0" * 32 

    # Extract the mantissa, exponent, and precision. Note that the 
    # values are slightly different than the IEEE 32-bit standard. 

    mnt, exp, prc = x.digits(2) 

    # MPFR explicitely stores the leading bit of the mantissa so the 
    # precision is 24. To support subnormals, MPFR also uses a more 
    # negative minimum exponent and decreases the precision of the 
    # mantissa but maintains the leading '1' bit. 

    # Remove any leading sign bit from the mantissa string. 
    if mnt[0] == "-": 
     sign_char = "1" 
     mnt = mnt[1:] 
    else: 
     sign_char = "0" 

    # Check for subnormals 
    if exp + 126 <= 0: 
     # Drop the last bit since it will always be '0' and after the 
     # adjustments for subnormals, the leading bit will be '0'. 
     mnt = mnt[:-1] 
    else: 
     # Drop the leading '1' bit for normal numbers. 
     mnt = mnt[1:] 

    # Handle subnormals by shifting trailing bits from the mantissa 
    # string to the beginning. Adjust the exponent to match. 
    while exp + 126 < 0: 
     mnt = mnt[-1] + mnt[:-1] 
     exp = exp + 1 

    # Adjust the exponent to account for removing a bit from the 
    # mantissa string. 
    exp = exp - 1 

    # Add 127 to the exponent to account for the IEEE encoding. 
    exp = exp + 127 

    # Validate exponent range. 
    if (exp > 255) or (exp < 0): 
     raise ValueError("exp is out of bounds") 

    # Build and return the binary string. 
    result = sign_char + format(exp, "08b") + mnt 
    if len(result) != 32: 
     raise ValueError("something is wrong....") 

    return result 


if __name__ == "__main__": 
    print(mpfr_to_float(gmpy2.mpfr("0.1"))) 

Haftungsausschluss # 1: Das ist wirklich ein Kommentar zu @Stefan Pochmann Antwort sein sollte, aber ich dachte, ein Codebeispiel wäre hilfreich.

Disclaimer # 2: Ich pflege gmpy2.

+0

Weniger lehrreich als die Darstellung mit 'struct.pack' /' struct.unpack' alleine, aber sicherlich einfacher/netter/sicherer zu benutzen. Und besser als mein 'ctypes.c_float (0.1) .value' Hack. Nur eine Frage: Bietet gmpy2 die Möglichkeit, die Bits der IEEE-Darstellung (wie auf der Webseite und im OP-Code) anzuzeigen? –

+0

@StefanPochmann Leider nicht. Die zugrunde liegende MPFR-Bibliothek verwendet eine andere interne Repräsentation. MPFR bietet Funktionen zum Konvertieren in IEEE-Float- und -Doppelwerte, aber ich lege die Float-Version nicht offen, da Python keinen nativen 32-Bit-Fließkommatyp bereitstellt. Ich werde versuchen, einen Python-mpfr-to-float-Konverter zu schreiben und meine Antwort zu bearbeiten. – casevh

+0

@StefanPochmann Beispielfunktion hinzugefügt. – casevh