cython-devel
changeset 2299:a3ad25325fe4
fix __future__ division semantics for constant expressions and C integers
| author | Stefan Behnel <scoder@users.berlios.de> |
|---|---|
| date | Wed Jul 08 21:13:14 2009 +0200 (12 months ago) |
| parents | 058620d97aeb |
| children | 495a6e5911e3 |
| files | Cython/Compiler/ExprNodes.py Cython/Compiler/Nodes.py Cython/Compiler/Parsing.py tests/run/future_division.pyx tests/run/non_future_division.pyx |
line diff
1.1 --- a/Cython/Compiler/ExprNodes.py Wed Jul 08 20:24:13 2009 +0200
1.2 +++ b/Cython/Compiler/ExprNodes.py Wed Jul 08 21:13:14 2009 +0200
1.3 @@ -728,9 +728,7 @@
1.4 type = PyrexTypes.c_double_type
1.5
1.6 def calculate_constant_result(self):
1.7 - # calculating float values is usually not a good idea
1.8 - #self.constant_result = float(self.value)
1.9 - pass
1.10 + self.constant_result = float(self.value)
1.11
1.12 def compile_time_value(self, denv):
1.13 return float(self.value)
1.14 @@ -4111,7 +4109,7 @@
1.15 'is_not': operator.is_not,
1.16 '+': operator.add,
1.17 '&': operator.and_,
1.18 - '/': operator.div,
1.19 + '/': operator.truediv,
1.20 '//': operator.floordiv,
1.21 '<<': operator.lshift,
1.22 '%': operator.mod,
1.23 @@ -4120,7 +4118,6 @@
1.24 '**': operator.pow,
1.25 '>>': operator.rshift,
1.26 '-': operator.sub,
1.27 - #'/': operator.truediv,
1.28 '^': operator.xor,
1.29 'in': operator.contains,
1.30 'not_in': _not_in,
1.31 @@ -4278,13 +4275,13 @@
1.32 "|": "PyNumber_Or",
1.33 "^": "PyNumber_Xor",
1.34 "&": "PyNumber_And",
1.35 - "<<": "PyNumber_Lshift",
1.36 - ">>": "PyNumber_Rshift",
1.37 + "<<": "PyNumber_Lshift",
1.38 + ">>": "PyNumber_Rshift",
1.39 "+": "PyNumber_Add",
1.40 "-": "PyNumber_Subtract",
1.41 "*": "PyNumber_Multiply",
1.42 "/": "__Pyx_PyNumber_Divide",
1.43 - "//": "PyNumber_FloorDivide",
1.44 + "//": "PyNumber_FloorDivide",
1.45 "%": "PyNumber_Remainder",
1.46 "**": "PyNumber_Power"
1.47 }
1.48 @@ -4350,20 +4347,63 @@
1.49 # '/' or '//' operator.
1.50
1.51 cdivision = None
1.52 + truedivision = None # == "unknown" if operator == '/'
1.53 + ctruedivision = False
1.54 cdivision_warnings = False
1.55 zerodivision_check = None
1.56 -
1.57 - def analyse_types(self, env):
1.58 +
1.59 + def find_compile_time_binary_operator(self, op1, op2):
1.60 + func = compile_time_binary_operators[self.operator]
1.61 + if self.operator == '/' and self.truedivision is None:
1.62 + # => true div for floats, floor div for integers
1.63 + if isinstance(op1, (int,long)) and isinstance(op2, (int,long)):
1.64 + func = compile_time_binary_operators['//']
1.65 + return func
1.66 +
1.67 + def calculate_constant_result(self):
1.68 + op1 = self.operand1.constant_result
1.69 + op2 = self.operand2.constant_result
1.70 + func = self.find_compile_time_binary_operator(op1, op2)
1.71 + self.constant_result = func(
1.72 + self.operand1.constant_result,
1.73 + self.operand2.constant_result)
1.74 +
1.75 + def compile_time_value(self, denv):
1.76 + operand1 = self.operand1.compile_time_value(denv)
1.77 + operand2 = self.operand2.compile_time_value(denv)
1.78 + try:
1.79 + func = self.find_compile_time_binary_operator(
1.80 + self, operand1, operand2)
1.81 + return func(operand1, operand2)
1.82 + except Exception, e:
1.83 + self.compile_time_value_error(e)
1.84 +
1.85 + def analyse_types(self, env):
1.86 + if self.cdivision or env.directives['cdivision']:
1.87 + self.ctruedivision = False
1.88 + else:
1.89 + self.ctruedivision = self.truedivision
1.90 NumBinopNode.analyse_types(self, env)
1.91 if not self.type.is_pyobject:
1.92 - self.zerodivision_check = self.cdivision is None and not env.directives['cdivision']
1.93 + self.zerodivision_check = (
1.94 + self.cdivision is None and not env.directives['cdivision']
1.95 + and (self.operand2.constant_result is not_a_constant or
1.96 + self.operand2.constant_result == 0))
1.97 if self.zerodivision_check or env.directives['cdivision_warnings']:
1.98 # Need to check ahead of time to warn or raise zero division error
1.99 self.operand1 = self.operand1.coerce_to_simple(env)
1.100 self.operand2 = self.operand2.coerce_to_simple(env)
1.101 if env.nogil:
1.102 error(self.pos, "Pythonic division not allowed without gil, consider using cython.cdivision(True)")
1.103 -
1.104 +
1.105 + def compute_c_result_type(self, type1, type2):
1.106 + if self.operator == '/' and self.ctruedivision:
1.107 + if not type1.is_float and not type2.is_float:
1.108 + widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
1.109 + widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
1.110 + return widest_type
1.111 + return NumBinopNode.compute_c_result_type(self, type1, type2)
1.112 +
1.113 def zero_division_message(self):
1.114 if self.type.is_int:
1.115 return "integer division or modulo by zero"
1.116 @@ -4416,12 +4456,17 @@
1.117 return NumBinopNode.calculate_result_code(self)
1.118 elif self.type.is_float and self.operator == '//':
1.119 return "floor(%s / %s)" % (
1.120 - self.operand1.result(),
1.121 + self.operand1.result(),
1.122 self.operand2.result())
1.123 - elif self.cdivision:
1.124 - return "(%s / %s)" % (
1.125 - self.operand1.result(),
1.126 - self.operand2.result())
1.127 + elif self.truedivision or self.cdivision:
1.128 + op1 = self.operand1.result()
1.129 + op2 = self.operand2.result()
1.130 + if self.truedivision:
1.131 + if self.type != self.operand1.type:
1.132 + op1 = self.type.cast_code(op1)
1.133 + if self.type != self.operand2.type:
1.134 + op2 = self.type.cast_code(op2)
1.135 + return "(%s / %s)" % (op1, op2)
1.136 else:
1.137 return "__Pyx_div_%s(%s, %s)" % (
1.138 self.type.specalization_name(),
2.1 --- a/Cython/Compiler/Nodes.py Wed Jul 08 20:24:13 2009 +0200
2.2 +++ b/Cython/Compiler/Nodes.py Wed Jul 08 21:13:14 2009 +0200
2.3 @@ -734,7 +734,7 @@
2.4 self.positional_args,
2.5 self.keyword_args,
2.6 base_type.buffer_defaults)
2.7 -
2.8 +
2.9 self.type = PyrexTypes.BufferType(base_type, **options)
2.10 return self.type
2.11
3.1 --- a/Cython/Compiler/Parsing.py Wed Jul 08 20:24:13 2009 +0200
3.2 +++ b/Cython/Compiler/Parsing.py Wed Jul 08 21:13:14 2009 +0200
3.3 @@ -65,14 +65,23 @@
3.4 #
3.5 #------------------------------------------
3.6
3.7 +def p_binop_operator(s):
3.8 + pos = s.position()
3.9 + op = s.sy
3.10 + s.next()
3.11 + return op, pos
3.12 +
3.13 def p_binop_expr(s, ops, p_sub_expr):
3.14 n1 = p_sub_expr(s)
3.15 while s.sy in ops:
3.16 - op = s.sy
3.17 - pos = s.position()
3.18 - s.next()
3.19 + op, pos = p_binop_operator(s)
3.20 n2 = p_sub_expr(s)
3.21 n1 = ExprNodes.binop_node(pos, op, n1, n2)
3.22 + if op == '/':
3.23 + if Future.division in s.context.future_directives:
3.24 + n1.truedivision = True
3.25 + else:
3.26 + n1.truedivision = None # unknown
3.27 return n1
3.28
3.29 #expression: or_test [if or_test else test] | lambda_form
4.1 --- a/tests/run/future_division.pyx Wed Jul 08 20:24:13 2009 +0200
4.2 +++ b/tests/run/future_division.pyx Wed Jul 08 21:13:14 2009 +0200
4.3 @@ -1,7 +1,6 @@
4.4 from __future__ import division
4.5
4.6 __doc__ = u"""
4.7 ->>> from future_division import doit
4.8 >>> doit(1,2)
4.9 (0.5, 0)
4.10 >>> doit(4,3)
4.11 @@ -10,7 +9,55 @@
4.12 (1.3333333333333333, 1.0)
4.13 >>> doit(4,2)
4.14 (2.0, 2)
4.15 +
4.16 +>>> constants()
4.17 +(0.5, 0, 2.5, 2.0, 2.5, 2)
4.18 +
4.19 +>>> py_mix(1)
4.20 +(0.5, 0, 0.5, 0.0, 0.5, 0)
4.21 +
4.22 +>>> py_mix_rev(4)
4.23 +(0.25, 0, 1.25, 1.0, 1.25, 1)
4.24 +
4.25 +>>> py_mix(1.0)
4.26 +(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
4.27 +
4.28 +>>> py_mix_rev(4.0)
4.29 +(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
4.30 +
4.31 +>>> int_mix(1)
4.32 +(0.5, 0, 0.5, 0.0, 0.5, 0)
4.33 +
4.34 +>>> int_mix_rev(4)
4.35 +(0.25, 0, 1.25, 1.0, 1.25, 1)
4.36 +
4.37 +>>> float_mix(1.0)
4.38 +(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
4.39 +
4.40 +>>> float_mix_rev(4.0)
4.41 +(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
4.42 """
4.43
4.44 def doit(x,y):
4.45 return x/y, x//y
4.46 +
4.47 +def constants():
4.48 + return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2
4.49 +
4.50 +def py_mix(a):
4.51 + return a/2, a//2, a/2.0, a//2.0, a/2, a//2
4.52 +
4.53 +def py_mix_rev(a):
4.54 + return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
4.55 +
4.56 +def int_mix(int a):
4.57 + return a/2, a//2, a/2.0, a//2.0, a/2, a//2
4.58 +
4.59 +def int_mix_rev(int a):
4.60 + return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
4.61 +
4.62 +def float_mix(float a):
4.63 + return a/2, a//2, a/2.0, a//2.0, a/2, a//2
4.64 +
4.65 +def float_mix_rev(float a):
4.66 + return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/tests/run/non_future_division.pyx Wed Jul 08 21:13:14 2009 +0200
5.3 @@ -0,0 +1,63 @@
5.4 +# Py2.x mixed true-div/floor-div behaviour of '/' operator
5.5 +
5.6 +__doc__ = u"""
5.7 +>>> doit(1,2)
5.8 +(0, 0)
5.9 +>>> doit(4,3)
5.10 +(1, 1)
5.11 +>>> doit(4,3.0)
5.12 +(1.3333333333333333, 1.0)
5.13 +>>> doit(4,2)
5.14 +(2, 2)
5.15 +
5.16 +>>> constants()
5.17 +(0, 0, 2.5, 2.0, 2, 2)
5.18 +
5.19 +>>> py_mix(1)
5.20 +(0, 0, 0.5, 0.0, 0, 0)
5.21 +
5.22 +>>> py_mix_rev(4)
5.23 +(0, 0, 1.25, 1.0, 1, 1)
5.24 +
5.25 +>>> py_mix(1.0)
5.26 +(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
5.27 +
5.28 +>>> py_mix_rev(4.0)
5.29 +(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
5.30 +
5.31 +>>> int_mix(1)
5.32 +(0, 0, 0.5, 0.0, 0, 0)
5.33 +
5.34 +>>> int_mix_rev(4)
5.35 +(0, 0, 1.25, 1.0, 1, 1)
5.36 +
5.37 +>>> float_mix(1.0)
5.38 +(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
5.39 +
5.40 +>>> float_mix_rev(4.0)
5.41 +(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
5.42 +"""
5.43 +
5.44 +def doit(x,y):
5.45 + return x/y, x//y
5.46 +
5.47 +def constants():
5.48 + return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2
5.49 +
5.50 +def py_mix(a):
5.51 + return a/2, a//2, a/2.0, a//2.0, a/2, a//2
5.52 +
5.53 +def py_mix_rev(a):
5.54 + return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
5.55 +
5.56 +def int_mix(int a):
5.57 + return a/2, a//2, a/2.0, a//2.0, a/2, a//2
5.58 +
5.59 +def int_mix_rev(int a):
5.60 + return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
5.61 +
5.62 +def float_mix(float a):
5.63 + return a/2, a//2, a/2.0, a//2.0, a/2, a//2
5.64 +
5.65 +def float_mix_rev(float a):
5.66 + return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
