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