File: longlong2float.py

package info (click to toggle)
pypy 5.6.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 97,040 kB
  • ctags: 185,069
  • sloc: python: 1,147,862; ansic: 49,642; cpp: 5,245; asm: 5,169; makefile: 529; sh: 481; xml: 232; lisp: 45
file content (145 lines) | stat: -rw-r--r-- 5,504 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""
This module exposes the functions longlong2float() and float2longlong(),
which cast the bit pattern of a float into a long long and back.
Warning: don't use in the other direction, i.e. don't cast a random
long long to a float and back to a long long.  There are corner cases
in which it does not work.
"""

from __future__ import with_statement
import sys
from rpython.annotator import model as annmodel
from rpython.rlib.rarithmetic import r_int64, intmask
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.extregistry import ExtRegistryEntry
from rpython.translator.tool.cbuild import ExternalCompilationInfo


# -------- implement longlong2float and float2longlong --------
DOUBLE_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.DOUBLE))
LONGLONG_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.LONGLONG))
UINT_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.UINT))
FLOAT_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.FLOAT))

# these definitions are used only in tests, when not translated
def longlong2float(llval):
    with lltype.scoped_alloc(DOUBLE_ARRAY_PTR.TO, 1) as d_array:
        ll_array = rffi.cast(LONGLONG_ARRAY_PTR, d_array)
        ll_array[0] = llval
        floatval = d_array[0]
        return floatval

def float2longlong(floatval):
    with lltype.scoped_alloc(DOUBLE_ARRAY_PTR.TO, 1) as d_array:
        ll_array = rffi.cast(LONGLONG_ARRAY_PTR, d_array)
        d_array[0] = floatval
        llval = ll_array[0]
        return llval

def uint2singlefloat_emulator(ival):
    with lltype.scoped_alloc(FLOAT_ARRAY_PTR.TO, 1) as f_array:
        i_array = rffi.cast(UINT_ARRAY_PTR, f_array)
        i_array[0] = ival
        singlefloatval = f_array[0]
        return singlefloatval

def singlefloat2uint_emulator(singlefloatval):
    with lltype.scoped_alloc(FLOAT_ARRAY_PTR.TO, 1) as f_array:
        i_array = rffi.cast(UINT_ARRAY_PTR, f_array)
        f_array[0] = singlefloatval
        ival = i_array[0]
        return ival

eci = ExternalCompilationInfo(includes=['string.h', 'assert.h'],
                              post_include_bits=["""
static float pypy__uint2singlefloat(unsigned int x) {
    float ff;
    assert(sizeof(float) == 4 && sizeof(unsigned int) == 4);
    memcpy(&ff, &x, 4);
    return ff;
}
static unsigned int pypy__singlefloat2uint(float x) {
    unsigned int ii;
    assert(sizeof(float) == 4 && sizeof(unsigned int) == 4);
    memcpy(&ii, &x, 4);
    return ii;
}
"""])

uint2singlefloat = rffi.llexternal(
    "pypy__uint2singlefloat", [rffi.UINT], rffi.FLOAT,
    _callable=uint2singlefloat_emulator, compilation_info=eci,
    _nowrapper=True, elidable_function=True, sandboxsafe=True)

singlefloat2uint = rffi.llexternal(
    "pypy__singlefloat2uint", [rffi.FLOAT], rffi.UINT,
    _callable=singlefloat2uint_emulator, compilation_info=eci,
    _nowrapper=True, elidable_function=True, sandboxsafe=True)


class Float2LongLongEntry(ExtRegistryEntry):
    _about_ = float2longlong

    def compute_result_annotation(self, s_float):
        assert annmodel.SomeFloat().contains(s_float)
        return annmodel.SomeInteger(knowntype=r_int64)

    def specialize_call(self, hop):
        [v_float] = hop.inputargs(lltype.Float)
        hop.exception_cannot_occur()
        return hop.genop("convert_float_bytes_to_longlong", [v_float], resulttype=lltype.SignedLongLong)

class LongLong2FloatEntry(ExtRegistryEntry):
    _about_ = longlong2float

    def compute_result_annotation(self, s_longlong):
        assert annmodel.SomeInteger(knowntype=r_int64).contains(s_longlong)
        return annmodel.SomeFloat()

    def specialize_call(self, hop):
        [v_longlong] = hop.inputargs(lltype.SignedLongLong)
        hop.exception_cannot_occur()
        return hop.genop("convert_longlong_bytes_to_float", [v_longlong], resulttype=lltype.Float)

# ____________________________________________________________


# For encoding integers inside nonstandard NaN bit patterns.
#   ff ff ff fe xx xx xx xx    (signed 32-bit int)
nan_high_word_int32 = -2       # -2 == (int)0xfffffffe
nan_encoded_zero = r_int64(nan_high_word_int32 << 32)

def encode_int32_into_longlong_nan(value):
    return (nan_encoded_zero +
            rffi.cast(rffi.LONGLONG, rffi.cast(rffi.UINT, value)))

def decode_int32_from_longlong_nan(value):
    return rffi.cast(lltype.Signed, rffi.cast(rffi.INT, value))

def is_int32_from_longlong_nan(value):
    return intmask(value >> 32) == nan_high_word_int32

CAN_ALWAYS_ENCODE_INT32 = (sys.maxint == 2147483647)

def can_encode_int32(value):
    if CAN_ALWAYS_ENCODE_INT32:
        return True
    return value == rffi.cast(lltype.Signed, rffi.cast(rffi.INT, value))

def can_encode_float(value):
    return intmask(float2longlong(value) >> 32) != nan_high_word_int32

def maybe_decode_longlong_as_float(value):
    # Decode a longlong value.  If a float, just return it as a float.
    # If an encoded integer, return a float that has the (exact) same
    # value.  This relies on the fact that casting from a decoded int
    # to a float is an exact operation.  If we had full 64-bit
    # integers, this cast would loose precision.  But this works
    # because the integers are only 32-bit.  This would also work even
    # if we encoded larger integers: as long as they are encoded
    # inside a subset of the mantissa of a float, then the
    # cast-to-float will be exact.
    if is_int32_from_longlong_nan(value):
        return float(decode_int32_from_longlong_nan(value))
    else:
        return longlong2float(value)