Commit 3d2aa773 authored by Stefan Behnel's avatar Stefan Behnel

fix __future__ division semantics for constant expressions and C integers

--HG--
rename : tests/run/future_division.pyx => tests/run/non_future_division.pyx
parent eacc6921
...@@ -728,9 +728,7 @@ class FloatNode(ConstNode): ...@@ -728,9 +728,7 @@ class FloatNode(ConstNode):
type = PyrexTypes.c_double_type type = PyrexTypes.c_double_type
def calculate_constant_result(self): def calculate_constant_result(self):
# calculating float values is usually not a good idea self.constant_result = float(self.value)
#self.constant_result = float(self.value)
pass
def compile_time_value(self, denv): def compile_time_value(self, denv):
return float(self.value) return float(self.value)
...@@ -4111,7 +4109,7 @@ compile_time_binary_operators = { ...@@ -4111,7 +4109,7 @@ compile_time_binary_operators = {
'is_not': operator.is_not, 'is_not': operator.is_not,
'+': operator.add, '+': operator.add,
'&': operator.and_, '&': operator.and_,
'/': operator.div, '/': operator.truediv,
'//': operator.floordiv, '//': operator.floordiv,
'<<': operator.lshift, '<<': operator.lshift,
'%': operator.mod, '%': operator.mod,
...@@ -4120,7 +4118,6 @@ compile_time_binary_operators = { ...@@ -4120,7 +4118,6 @@ compile_time_binary_operators = {
'**': operator.pow, '**': operator.pow,
'>>': operator.rshift, '>>': operator.rshift,
'-': operator.sub, '-': operator.sub,
#'/': operator.truediv,
'^': operator.xor, '^': operator.xor,
'in': operator.contains, 'in': operator.contains,
'not_in': _not_in, 'not_in': _not_in,
...@@ -4278,13 +4275,13 @@ class NumBinopNode(BinopNode): ...@@ -4278,13 +4275,13 @@ class NumBinopNode(BinopNode):
"|": "PyNumber_Or", "|": "PyNumber_Or",
"^": "PyNumber_Xor", "^": "PyNumber_Xor",
"&": "PyNumber_And", "&": "PyNumber_And",
"<<": "PyNumber_Lshift", "<<": "PyNumber_Lshift",
">>": "PyNumber_Rshift", ">>": "PyNumber_Rshift",
"+": "PyNumber_Add", "+": "PyNumber_Add",
"-": "PyNumber_Subtract", "-": "PyNumber_Subtract",
"*": "PyNumber_Multiply", "*": "PyNumber_Multiply",
"/": "__Pyx_PyNumber_Divide", "/": "__Pyx_PyNumber_Divide",
"//": "PyNumber_FloorDivide", "//": "PyNumber_FloorDivide",
"%": "PyNumber_Remainder", "%": "PyNumber_Remainder",
"**": "PyNumber_Power" "**": "PyNumber_Power"
} }
...@@ -4350,20 +4347,63 @@ class DivNode(NumBinopNode): ...@@ -4350,20 +4347,63 @@ class DivNode(NumBinopNode):
# '/' or '//' operator. # '/' or '//' operator.
cdivision = None cdivision = None
truedivision = None # == "unknown" if operator == '/'
ctruedivision = False
cdivision_warnings = False cdivision_warnings = False
zerodivision_check = None zerodivision_check = None
def find_compile_time_binary_operator(self, op1, op2):
func = compile_time_binary_operators[self.operator]
if self.operator == '/' and self.truedivision is None:
# => true div for floats, floor div for integers
if isinstance(op1, (int,long)) and isinstance(op2, (int,long)):
func = compile_time_binary_operators['//']
return func
def calculate_constant_result(self):
op1 = self.operand1.constant_result
op2 = self.operand2.constant_result
func = self.find_compile_time_binary_operator(op1, op2)
self.constant_result = func(
self.operand1.constant_result,
self.operand2.constant_result)
def compile_time_value(self, denv):
operand1 = self.operand1.compile_time_value(denv)
operand2 = self.operand2.compile_time_value(denv)
try:
func = self.find_compile_time_binary_operator(
self, operand1, operand2)
return func(operand1, operand2)
except Exception, e:
self.compile_time_value_error(e)
def analyse_types(self, env): def analyse_types(self, env):
if self.cdivision or env.directives['cdivision']:
self.ctruedivision = False
else:
self.ctruedivision = self.truedivision
NumBinopNode.analyse_types(self, env) NumBinopNode.analyse_types(self, env)
if not self.type.is_pyobject: if not self.type.is_pyobject:
self.zerodivision_check = self.cdivision is None and not env.directives['cdivision'] self.zerodivision_check = (
self.cdivision is None and not env.directives['cdivision']
and (self.operand2.constant_result is not_a_constant or
self.operand2.constant_result == 0))
if self.zerodivision_check or env.directives['cdivision_warnings']: if self.zerodivision_check or env.directives['cdivision_warnings']:
# Need to check ahead of time to warn or raise zero division error # Need to check ahead of time to warn or raise zero division error
self.operand1 = self.operand1.coerce_to_simple(env) self.operand1 = self.operand1.coerce_to_simple(env)
self.operand2 = self.operand2.coerce_to_simple(env) self.operand2 = self.operand2.coerce_to_simple(env)
if env.nogil: if env.nogil:
error(self.pos, "Pythonic division not allowed without gil, consider using cython.cdivision(True)") error(self.pos, "Pythonic division not allowed without gil, consider using cython.cdivision(True)")
def compute_c_result_type(self, type1, type2):
if self.operator == '/' and self.ctruedivision:
if not type1.is_float and not type2.is_float:
widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
return widest_type
return NumBinopNode.compute_c_result_type(self, type1, type2)
def zero_division_message(self): def zero_division_message(self):
if self.type.is_int: if self.type.is_int:
return "integer division or modulo by zero" return "integer division or modulo by zero"
...@@ -4416,12 +4456,17 @@ class DivNode(NumBinopNode): ...@@ -4416,12 +4456,17 @@ class DivNode(NumBinopNode):
return NumBinopNode.calculate_result_code(self) return NumBinopNode.calculate_result_code(self)
elif self.type.is_float and self.operator == '//': elif self.type.is_float and self.operator == '//':
return "floor(%s / %s)" % ( return "floor(%s / %s)" % (
self.operand1.result(), self.operand1.result(),
self.operand2.result())
elif self.cdivision:
return "(%s / %s)" % (
self.operand1.result(),
self.operand2.result()) self.operand2.result())
elif self.truedivision or self.cdivision:
op1 = self.operand1.result()
op2 = self.operand2.result()
if self.truedivision:
if self.type != self.operand1.type:
op1 = self.type.cast_code(op1)
if self.type != self.operand2.type:
op2 = self.type.cast_code(op2)
return "(%s / %s)" % (op1, op2)
else: else:
return "__Pyx_div_%s(%s, %s)" % ( return "__Pyx_div_%s(%s, %s)" % (
self.type.specalization_name(), self.type.specalization_name(),
......
...@@ -734,7 +734,7 @@ class CBufferAccessTypeNode(CBaseTypeNode): ...@@ -734,7 +734,7 @@ class CBufferAccessTypeNode(CBaseTypeNode):
self.positional_args, self.positional_args,
self.keyword_args, self.keyword_args,
base_type.buffer_defaults) base_type.buffer_defaults)
self.type = PyrexTypes.BufferType(base_type, **options) self.type = PyrexTypes.BufferType(base_type, **options)
return self.type return self.type
......
...@@ -65,14 +65,23 @@ def p_ident_list(s): ...@@ -65,14 +65,23 @@ def p_ident_list(s):
# #
#------------------------------------------ #------------------------------------------
def p_binop_operator(s):
pos = s.position()
op = s.sy
s.next()
return op, pos
def p_binop_expr(s, ops, p_sub_expr): def p_binop_expr(s, ops, p_sub_expr):
n1 = p_sub_expr(s) n1 = p_sub_expr(s)
while s.sy in ops: while s.sy in ops:
op = s.sy op, pos = p_binop_operator(s)
pos = s.position()
s.next()
n2 = p_sub_expr(s) n2 = p_sub_expr(s)
n1 = ExprNodes.binop_node(pos, op, n1, n2) n1 = ExprNodes.binop_node(pos, op, n1, n2)
if op == '/':
if Future.division in s.context.future_directives:
n1.truedivision = True
else:
n1.truedivision = None # unknown
return n1 return n1
#expression: or_test [if or_test else test] | lambda_form #expression: or_test [if or_test else test] | lambda_form
......
from __future__ import division from __future__ import division
__doc__ = u""" __doc__ = u"""
>>> from future_division import doit
>>> doit(1,2) >>> doit(1,2)
(0.5, 0) (0.5, 0)
>>> doit(4,3) >>> doit(4,3)
...@@ -10,7 +9,55 @@ __doc__ = u""" ...@@ -10,7 +9,55 @@ __doc__ = u"""
(1.3333333333333333, 1.0) (1.3333333333333333, 1.0)
>>> doit(4,2) >>> doit(4,2)
(2.0, 2) (2.0, 2)
>>> constants()
(0.5, 0, 2.5, 2.0, 2.5, 2)
>>> py_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
>>> py_mix_rev(4)
(0.25, 0, 1.25, 1.0, 1.25, 1)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> py_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
>>> int_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
>>> int_mix_rev(4)
(0.25, 0, 1.25, 1.0, 1.25, 1)
>>> float_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> float_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
""" """
def doit(x,y): def doit(x,y):
return x/y, x//y return x/y, x//y
def constants():
return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2
def py_mix(a):
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def py_mix_rev(a):
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def int_mix(int a):
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def int_mix_rev(int a):
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def float_mix(float a):
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def float_mix_rev(float a):
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
# Py2.x mixed true-div/floor-div behaviour of '/' operator
__doc__ = u"""
>>> doit(1,2)
(0, 0)
>>> doit(4,3)
(1, 1)
>>> doit(4,3.0)
(1.3333333333333333, 1.0)
>>> doit(4,2)
(2, 2)
>>> constants()
(0, 0, 2.5, 2.0, 2, 2)
>>> py_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
>>> py_mix_rev(4)
(0, 0, 1.25, 1.0, 1, 1)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> py_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
>>> int_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
>>> int_mix_rev(4)
(0, 0, 1.25, 1.0, 1, 1)
>>> float_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> float_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
def doit(x,y):
return x/y, x//y
def constants():
return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2
def py_mix(a):
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def py_mix_rev(a):
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def int_mix(int a):
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def int_mix_rev(int a):
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def float_mix(float a):
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def float_mix_rev(float a):
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment