[pypy-svn] r75948 - pypy/branch/fast-forward/lib_pypy
benjamin at codespeak.net
benjamin at codespeak.net
Wed Jul 7 01:49:00 CEST 2010
Author: benjamin
Date: Wed Jul 7 01:48:59 2010
New Revision: 75948
Modified:
pypy/branch/fast-forward/lib_pypy/struct.py
Log:
fix float packing/unpacking
Modified: pypy/branch/fast-forward/lib_pypy/struct.py
==============================================================================
--- pypy/branch/fast-forward/lib_pypy/struct.py (original)
+++ pypy/branch/fast-forward/lib_pypy/struct.py Wed Jul 7 01:48:59 2010
@@ -66,42 +66,6 @@
INFINITY = 1e200 * 1e200
NAN = INFINITY / INFINITY
-def unpack_float(data,index,size,le):
- bytes = [ord(b) for b in data[index:index+size]]
- if len(bytes) != size:
- raise StructError,"Not enough data to unpack"
- if max(bytes) == 0:
- return 0.0
- if le == 'big':
- bytes.reverse()
- if size == 4:
- bias = 127
- exp = 8
- prec = 23
- else:
- bias = 1023
- exp = 11
- prec = 52
- mantissa = long(bytes[size-2] & (2**(15-exp)-1))
- for b in bytes[size-3::-1]:
- mantissa = mantissa << 8 | b
- mantissa = 1 + (1.0*mantissa)/(2**(prec))
- mantissa /= 2
- e = (bytes[-1] & 0x7f) << (exp - 7)
- e += (bytes[size-2] >> (15 - exp)) & (2**(exp - 7) -1)
- e -= bias
- e += 1
- sign = bytes[-1] & 0x80
- if e == bias + 2:
- if mantissa == 0.5:
- number = INFINITY
- else:
- return NAN
- else:
- number = math.ldexp(mantissa,e)
- if sign : number *= -1
- return number
-
def unpack_char(data,index,size,le):
return data[index:index+size]
@@ -139,54 +103,138 @@
def isnan(v):
return v != v*1.0 or (v == 1.0 and v == 2.0)
-def pack_float(number, size, le):
- if size == 4:
- bias = 127
- exp = 8
- prec = 23
+def pack_float(x, size, le):
+ unsigned = float_pack(x, size)
+ result = []
+ for i in range(8):
+ result.append(chr((unsigned >> (i * 8)) & 0xFF))
+ if le == "big":
+ result.reverse()
+ return ''.join(result)
+
+def unpack_float(data, index, size, le):
+ binary = data[index:index + 8]
+ if le == "big":
+ binary.reverse()
+ unsigned = 0
+ for i in range(8):
+ unsigned |= ord(binary[i]) << (i * 8)
+ return float_unpack(unsigned, size)
+
+def round_to_nearest(x):
+ """Python 3 style round: round a float x to the nearest int, but
+ unlike the builtin Python 2.x round function:
+
+ - return an int, not a float
+ - do round-half-to-even, not round-half-away-from-zero.
+
+ We assume that x is finite and nonnegative; except wrong results
+ if you use this for negative x.
+
+ """
+ int_part = int(x)
+ frac_part = x - int_part
+ if frac_part > 0.5 or frac_part == 0.5 and int_part & 1 == 1:
+ int_part += 1
+ return int_part
+
+def float_unpack(Q, size, le):
+ """Convert a 32-bit or 64-bit integer created
+ by float_pack into a Python float."""
+
+ if size == 8:
+ MIN_EXP = -1021 # = sys.float_info.min_exp
+ MAX_EXP = 1024 # = sys.float_info.max_exp
+ MANT_DIG = 53 # = sys.float_info.mant_dig
+ BITS = 64
+ elif size == 4:
+ MIN_EXP = -125 # C's FLT_MIN_EXP
+ MAX_EXP = 128 # FLT_MAX_EXP
+ MANT_DIG = 24 # FLT_MANT_DIG
+ BITS = 32
else:
- bias = 1023
- exp = 11
- prec = 52
-
- if isnan(number):
- sign = 0x80
- man, e = 1.5, bias + 1
+ raise ValueError("invalid size value")
+
+ if Q >> BITS:
+ raise ValueError("input out of range")
+
+ # extract pieces
+ sign = Q >> BITS - 1
+ exp = (Q & ((1 << BITS - 1) - (1 << MANT_DIG - 1))) >> MANT_DIG - 1
+ mant = Q & ((1 << MANT_DIG - 1) - 1)
+
+ if exp == MAX_EXP - MIN_EXP + 2:
+ # nan or infinity
+ result = float('nan') if mant else float('inf')
+ elif exp == 0:
+ # subnormal or zero
+ result = math.ldexp(float(mant), MIN_EXP - MANT_DIG)
else:
- if number < 0:
- sign = 0x80
- number *= -1
- elif number == 0.0:
- return '\x00' * size
- else:
- sign = 0x00
- if isinf(number):
- man, e = 1.0, bias + 1
+ # normal
+ mant += 1 << MANT_DIG - 1
+ result = math.ldexp(float(mant), exp + MIN_EXP - MANT_DIG - 1)
+ return -result if sign else result
+
+
+def float_pack(x, size):
+ """Convert a Python float x into a 64-bit unsigned integer
+ with the same byte representation."""
+
+ if size == 8:
+ MIN_EXP = -1021 # = sys.float_info.min_exp
+ MAX_EXP = 1024 # = sys.float_info.max_exp
+ MANT_DIG = 53 # = sys.float_info.mant_dig
+ BITS = 64
+ elif size == 4:
+ MIN_EXP = -125 # C's FLT_MIN_EXP
+ MAX_EXP = 128 # FLT_MAX_EXP
+ MANT_DIG = 24 # FLT_MANT_DIG
+ BITS = 32
+ else:
+ raise ValueError("invalid size value")
+
+ sign = math.copysign(1.0, x) < 0.0
+ if math.isinf(x):
+ mant = 0
+ exp = MAX_EXP - MIN_EXP + 2
+ elif math.isnan(x):
+ mant = 1 << (MANT_DIG-2) # other values possible
+ exp = MAX_EXP - MIN_EXP + 2
+ elif x == 0.0:
+ mant = 0
+ exp = 0
+ else:
+ m, e = math.frexp(abs(x)) # abs(x) == m * 2**e
+ exp = e - (MIN_EXP - 1)
+ if exp > 0:
+ # Normal case.
+ mant = round_to_nearest(m * (1 << MANT_DIG))
+ mant -= 1 << MANT_DIG - 1
else:
- man, e = math.frexp(number)
+ # Subnormal case.
+ if exp + MANT_DIG - 1 >= 0:
+ mant = round_to_nearest(m * (1 << exp + MANT_DIG - 1))
+ else:
+ mant = 0
+ exp = 0
+
+ # Special case: rounding produced a MANT_DIG-bit mantissa.
+ assert 0 <= mant <= 1 << MANT_DIG - 1
+ if mant == 1 << MANT_DIG - 1:
+ mant = 0
+ exp += 1
+
+ # Raise on overflow (in some circumstances, may want to return
+ # infinity instead).
+ if exp >= MAX_EXP - MIN_EXP + 2:
+ raise OverflowError("float too large to pack in this format")
+
+ # check constraints
+ assert 0 <= mant < 1 << MANT_DIG - 1
+ assert 0 <= exp <= MAX_EXP - MIN_EXP + 2
+ assert 0 <= sign <= 1
+ return ((sign << BITS - 1) | (exp << MANT_DIG - 1)) | mant
- result = []
- if 0.5 <= man and man < 1.0:
- man *= 2
- e -= 1
- man -= 1
- e += bias
- power_of_two = 1 << prec
- mantissa = int(power_of_two * man + 0.5)
- if mantissa >> prec :
- mantissa = 0
- e += 1
-
- for i in range(size-2):
- result.append(chr(mantissa & 0xff))
- mantissa >>= 8
- x = (mantissa & ((1<<(15-exp))-1)) | ((e & ((1<<(exp-7))-1))<<(15-exp))
- result.append(chr(x))
- x = sign | e >> (exp - 7)
- result.append(chr(x))
- if le == 'big':
- result.reverse()
- return ''.join(result)
big_endian_format = {
'x':{ 'size' : 1, 'alignment' : 0, 'pack' : None, 'unpack' : None},
More information about the Pypy-commit
mailing list