-
Notifications
You must be signed in to change notification settings - Fork 0
/
number.py
155 lines (107 loc) · 4.73 KB
/
number.py
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
146
147
148
149
150
151
152
153
154
155
SCI_DECIMALS = 2
def set_precision(n):
global SCI_DECIMALS
assert isinstance(n, int) and n >= 0, "Precision must be a non-negative integer"
SCI_DECIMALS = n
def sci(number):
return '{:.{d}e}'.format(number, d=SCI_DECIMALS)
class _Scientific:
EQUAL_THRESH = 1e-12
# Reverse factory method: Converts _Scientific numbers to regular ones
@staticmethod
def _undo(x):
if isinstance(x, complex):
return complex(x)
else:
return float(x)
# True overrides
def __init__(self, over_type):
self._over_type = over_type
def tolerant_equal(self, value):
return float.__lt__(abs(self - value), abs(self * ScientificFloat.EQUAL_THRESH))
def approx(self, value, eps=1e-6):
return float.__lt__(abs(self - value), abs(self * eps))
def __eq__(self, value):
return self.tolerant_equal(value)
def __neq__(self, value):
return not self.tolerant_equal(value)
def __lt__(self, value):
return self._over_type.__lt__(self._over_type(self), _Scientific._undo(value)) and not self.tolerant_equal(value)
def __gt__(self, value):
return self._over_type.__gt__(self._over_type(self), _Scientific._undo(value)) and not self.tolerant_equal(value)
def __le__(self, value):
return self._over_type.__le__(self._over_type(self), _Scientific._undo(value)) or self.tolerant_equal(value)
def __ge__(self, value):
return self._over_type.__ge__(self._over_type(self), _Scientific._undo(value)) or self.tolerant_equal(value)
# Type up-conversions, that propagate the Scientific types through calculations
def __abs__(self):
return ScientificNumber( super().__abs__() )
def __add__(self, value):
return ScientificNumber(self._over_type(self) + value )
def __divmod__(self, value):
return ScientificNumber( divmod(self._over_type(self), value) )
def __floordiv__(self, value):
return ScientificNumber( self._over_type(self) // value )
def __mod__(self, value):
return ScientificNumber( self._over_type(self) % value )
def __mul__(self, value):
return ScientificNumber( self._over_type(self) * value )
def __neg__(self):
return ScientificNumber( -self._over_type(self) )
def __pos__(self):
return ScientificNumber( +self._over_type(self) )
def __pow__(self, value, mod=None):
return ScientificNumber( pow(self._over_type(self), value, mod) )
def __radd__(self, value):
return ScientificNumber( value + self._over_type(self) )
def __rdivmod__(self, value):
return ScientificNumber( divmod(value, self._over_type(self)) )
def __rfloordiv__(self, value):
return ScientificNumber( value // self._over_type(self) )
def __rmod__(self, value):
return ScientificNumber( value % self._over_type(self) )
def __rmul__(self, value):
return ScientificNumber( value * self._over_type(self) )
def __rpow__(self, value, mod=None):
return ScientificNumber( pow(value, self._over_type(self), mod) )
def __rsub__(self, value):
return ScientificNumber( value - self._over_type(self) )
def __rtruediv__(self, value):
return ScientificNumber( value / self._over_type(self) )
def __sub__(self, value):
return ScientificNumber( self._over_type(self) - value )
def __truediv__(self, value):
return ScientificNumber( self._over_type(self) / value )
def conjugate(*args, **kwargs):
return ScientificNumber( self._over_type(self).conjugate(*args, **kwargs) )
class ScientificFloat(_Scientific, float):
'''Floating point number that prints itself in scientific notation, and uses tolerant equality.'''
# Actual overrides:
def __new__(self, value):
return float.__new__(self, value)
def __init__(self, value):
_Scientific.__init__(self, float)
float.__init__(self)
def __str__(self):
return sci(self)
def __repr__(self):
return sci(self)
def fromhex(*args, **kwargs):
return ScientificNumber( super().fromhex(*args, **kwargs) )
class ScientificComplex(_Scientific, complex):
'''Complex floating point number that prints itself in scientific notation, and uses tolerant equality.'''
# Actual overrides:
def __new__(self, value):
return complex.__new__(self, value)
def __init__(self, value):
_Scientific.__init__(self, complex)
complex.__init__(self)
def __str__(self):
return '({} + {}j)'.format(sci(self.real), sci(self.imag))
def __repr__(self):
return str(self)
def ScientificNumber(x):
if isinstance(x, complex):
return ScientificComplex(x)
else:
return ScientificFloat(x)