So konvertieren Sie vorzeichenbehaftete in vorzeichenlose Ganzzahlen in Python


75

Nehmen wir an, ich habe diese Nummer i = -6884376. Wie beziehe ich mich auf eine vorzeichenlose Variable? So etwas wie (unsigned long)iin C.


9
Python hat keine vorzeichenlosen Typen eingebaut.
BrenBarn

2
Sie können die abs () Funktion verwenden.
Ali sagte OMAR

2
Was ist das Problem, das Sie damit lösen möchten?
BrenBarn

1
Der Integer-Typ von Python hat eine unbegrenzte Genauigkeit und ist nicht von den zugrunde liegenden Typen mit fester Größe abhängig. Daher kann nicht zwischen vorzeichenlosen und vorzeichenbehafteten Typen unterschieden werden. Sie müssen die Konvertierung selbst durchführen.
Poke

4
@AliSAIDOMAR nein, die absFunktion gibt nicht an, was die (unsigned)Besetzung in C gibt, was (uint32_t)a = (1LL << 32) - afür das Komplement von 2 ist
phuclv

Antworten:


95

Angenommen :

  1. Sie haben 2-Komplement-Darstellungen im Sinn; und,
  2. Damit meinen(unsigned long) Sie eine vorzeichenlose 32-Bit-Ganzzahl.

dann müssen Sie nur noch 2**32 (or 1 << 32)den negativen Wert addieren .

Wenden Sie dies beispielsweise auf -1 an:

>>> -1
-1
>>> _ + 2**32
4294967295L
>>> bin(_)
'0b11111111111111111111111111111111'

Annahme Nr. 1 bedeutet, dass -1 als feste Zeichenfolge mit 1 Bit angezeigt werden soll, und Annahme Nr. 2 bedeutet, dass Sie 32 davon wünschen.

Niemand außer Ihnen kann jedoch sagen, was Ihre verborgenen Annahmen sind. Wenn Sie beispielsweise 1-Komplement-Darstellungen im Auge haben, müssen Sie ~stattdessen den Präfix-Operator anwenden . Python-Ganzzahlen arbeiten hart, um die Illusion zu vermitteln, eine unendlich breite 2er-Komplementdarstellung zu verwenden (wie das reguläre 2er-Komplement, jedoch mit einer unendlichen Anzahl von "Vorzeichenbits").

Und um zu duplizieren, was der Plattform-C-Compiler tut, können Sie das ctypesModul verwenden:

>>> import ctypes
>>> ctypes.c_ulong(-1)  # stuff Python's -1 into a C unsigned long
c_ulong(4294967295L)
>>> _.value
4294967295L

Cs sind unsigned longzufällig 4 Bytes in der Box, in der dieses Beispiel ausgeführt wurde.


47
Ich wusste nicht, dass Sie _sich auf das Ergebnis der vorherigen Zeile beziehen können!
Yay295

@ Yay295 Richtig! Ist dies nur im interaktiven Modus möglich oder kann es auch im nicht interaktiven Modus verwendet werden?
HelloGoodbye

1
@HelloGoodbye, "_"das Abrufen eines vorherigen Ergebnisses ist keine Funktion der Sprache selbst, sondern eine Annehmlichkeit, die von einigen (fast allen) interaktiven Python-Shells implementiert wird. Sie müssen unabhängig davon das aktuellste Ergebnis abrufen, um es anzuzeigen. Die Sprache selbst macht so etwas nicht.
Tim Peters

67

Um den Wert zu erhalten, der Ihrem C-Cast entspricht, nur bitweise und mit der entsprechenden Maske. zB wenn unsigned long32 Bit ist:

>>> i = -6884376
>>> i & 0xffffffff
4288082920

oder wenn es 64 Bit ist:

>>> i & 0xffffffffffffffff
18446744073702667240

Beachten Sie jedoch, dass dies zwar den Wert ergibt, den Sie in C haben würden, es sich jedoch immer noch um einen vorzeichenbehafteten Wert handelt. Daher können nachfolgende Berechnungen zu einem negativen Ergebnis führen und Sie müssen die Maske weiterhin anwenden, um eine 32 oder 64 zu simulieren Bitberechnung.

Dies funktioniert, da Python zwar alle Zahlen als Vorzeichen und Größe speichert, die bitweisen Operationen jedoch so definiert sind, dass sie an den Zweierkomplementwerten arbeiten. C speichert ganze Zahlen in Zweierkomplement, jedoch mit einer festen Anzahl von Bits. Bitweise Python-Operatoren wirken auf Zweierkomplementwerte, aber als hätten sie eine unendliche Anzahl von Bits: Bei positiven Zahlen erstrecken sie sich mit Nullen nach links bis unendlich, negative Zahlen mit Einsen nach links. Der &Operator ändert diese linke Einsenfolge in Nullen und lässt Ihnen nur die Bits übrig, die in den C-Wert passen würden.

Das Anzeigen der Werte in hexadezimaler Darstellung kann dies klarer machen (und ich habe die Zeichenfolge von f als Ausdruck umgeschrieben, um zu zeigen, dass wir an 32 oder 64 Bit interessiert sind):

>>> hex(i)
'-0x690c18'
>>> hex (i & ((1 << 32) - 1))
'0xff96f3e8'
>>> hex (i & ((1 << 64) - 1)
'0xffffffffff96f3e8L'

Bei einem 32-Bit-Wert in C steigen positive Zahlen auf 2147483647 (0x7fffffff), und bei negativen Zahlen wird das oberste Bit von -1 (0xffffffff) auf -2147483648 (0x80000000) gesetzt. Für Werte, die vollständig in die Maske passen, können wir den Prozess in Python umkehren, indem wir eine kleinere Maske verwenden, um das Vorzeichenbit zu entfernen und dann das Vorzeichenbit zu subtrahieren:

>>> u = i & ((1 << 32) - 1)
>>> (u & ((1 << 31) - 1)) - (u & (1 << 31))
-6884376

Oder für die 64-Bit-Version:

>>> u = 18446744073702667240
>>> (u & ((1 << 63) - 1)) - (u & (1 << 63))
-6884376

Dieser inverse Prozess lässt den Wert unverändert, wenn das Vorzeichenbit 0 ist, aber offensichtlich ist es keine echte Inverse, denn wenn Sie mit einem Wert begonnen haben, der nicht in die Maskengröße passt, sind diese Bits weg.


+ Eins: Einfach, keine Importe
Petr Vepřek

1
Warum um alles in der Welt funktioniert das? (und was wäre die inverse Operation)
MB.

2
@ MB Ich habe meine Antwort erweitert, hoffe das hilft.
Duncan

Die Umkehrung hat sich als sehr nützlich erwiesen. Einige in C geschriebene Python-Bibliotheken geben einen vorzeichenbehafteten 64-Bit-Wert zurück, und dies endet als Long in Python
user1978816

16

Python hat keine vorzeichenlosen Typen eingebaut. Sie können mathematische Operationen verwenden, um ein neues int zu berechnen, das den Wert darstellt, den Sie in C erhalten würden, aber es gibt keinen "vorzeichenlosen Wert" eines Python-int. Das Python-Int ist eine Abstraktion eines ganzzahligen Werts, kein direkter Zugriff auf eine Ganzzahl mit fester Byte-Größe.


4

Sie können die in structPython integrierte Bibliothek verwenden:

Kodieren:

import struct

i = -6884376
print('{0:b}'.format(i))

packed = struct.pack('>l', i)  # Packing a long number.
unpacked = struct.unpack('>L', packed)[0]  # Unpacking a packed long number to unsigned long
print(unpacked)
print('{0:b}'.format(unpacked))

Aus:

-11010010000110000011000
4288082920
11111111100101101111001111101000

Dekodieren:

dec_pack = struct.pack('>L', unpacked)  # Packing an unsigned long number.
dec_unpack = struct.unpack('>l', dec_pack)[0]  # Unpacking a packed unsigned long number to long (revert action).
print(dec_unpack)

Aus:

-6884376

[ HINWEIS ]:

  • > ist BigEndian Operation.
  • l ist lang.
  • L ist lange nicht signiert.
  • In amd64Architektur intund longsind 32 - Bit, also Sie nutzen könnten iund Istatt lund Lsind.

1

Verwenden Sie einfach abs, um unsignierte in signierte in Python zu konvertieren

 a=-12
b=abs(a)
print(b)

Ausgabe: 12


Ich denke, das ist nur teilweise wahr. Wie im Python-MDN angegeben > abs() Gibt den absoluten Wert einer Zahl zurück. Das Argument kann eine einfache oder lange Ganzzahl oder eine Gleitkommazahl sein. Wenn das Argument eine komplexe Zahl ist, wird seine Größe zurückgegeben. Dies gibt per Definition nicht immer eine unsignedVariable zurück
Samuel Hulla

aber er bat nur um eine ganze Zahl.
Hari Prasath

1

Python verfügt nicht über eine integrierte Funktion zum Konvertieren von int in unsigned int. Stattdessen hat es eine lange Reichweite.

>>> val = 9223372036854775807 (maximum value of int 64)
>>> type(val)
<type 'int'>
>>> val += 1
>>> type(val)
<type 'long'>

Durch Erhöhen des Werts von val um 1 überschreite ich die Grenze einer vorzeichenbehafteten 64-Bit-Ganzzahl und der Wert wird in eine lange konvertiert. Wenn Python eine vorzeichenlose Ganzzahl verwendet oder konvertiert hätte, wäre val immer noch ein int gewesen. Oder nicht lange.

Vorzeichenbehaftete Ganzzahlen werden durch ein Bit dargestellt, normalerweise das höchstwertige Bit, das für positive Zahlen auf 0 oder für negative Zahlen auf 1 gesetzt wird. Was val & 0xff tut, ist tatsächlich val & 0x000000ff (auf einem 32-Bit-Computer). Mit anderen Worten wird das vorzeichenbehaftete Bit auf 0 gesetzt und ein vorzeichenloser Wert emuliert.

Ein Beispiel:

>>> val = -1
>>> val & 0xff
255

0

Seit Version 3.2:

def toSigned(n, byte_count): 
  return int.from_bytes(n.to_bytes(byte_count, 'little'), 'little', signed=True)

Ausgabe :

In [8]: toSigned(5, 1)                                                                                                                                                                                                                                                                                                     
Out[8]: 5

In [9]: toSigned(0xff, 1)                                                                                                                                                                                                                                                                                                  
Out[9]: -1
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.