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):
type = PyrexTypes.c_double_type
def calculate_constant_result(self):
# calculating float values is usually not a good idea
#self.constant_result = float(self.value)
pass
self.constant_result = float(self.value)
def compile_time_value(self, denv):
return float(self.value)
......@@ -4111,7 +4109,7 @@ compile_time_binary_operators = {
'is_not': operator.is_not,
'+': operator.add,
'&': operator.and_,
'/': operator.div,
'/': operator.truediv,
'//': operator.floordiv,
'<<': operator.lshift,
'%': operator.mod,
......@@ -4120,7 +4118,6 @@ compile_time_binary_operators = {
'**': operator.pow,
'>>': operator.rshift,
'-': operator.sub,
#'/': operator.truediv,
'^': operator.xor,
'in': operator.contains,
'not_in': _not_in,
......@@ -4350,13 +4347,48 @@ class DivNode(NumBinopNode):
# '/' or '//' operator.
cdivision = None
truedivision = None # == "unknown" if operator == '/'
ctruedivision = False
cdivision_warnings = False
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):
if self.cdivision or env.directives['cdivision']:
self.ctruedivision = False
else:
self.ctruedivision = self.truedivision
NumBinopNode.analyse_types(self, env)
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']:
# Need to check ahead of time to warn or raise zero division error
self.operand1 = self.operand1.coerce_to_simple(env)
......@@ -4364,6 +4396,14 @@ class DivNode(NumBinopNode):
if env.nogil:
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):
if self.type.is_int:
return "integer division or modulo by zero"
......@@ -4418,10 +4458,15 @@ class DivNode(NumBinopNode):
return "floor(%s / %s)" % (
self.operand1.result(),
self.operand2.result())
elif self.cdivision:
return "(%s / %s)" % (
self.operand1.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:
return "__Pyx_div_%s(%s, %s)" % (
self.type.specalization_name(),
......
......@@ -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):
n1 = p_sub_expr(s)
while s.sy in ops:
op = s.sy
pos = s.position()
s.next()
op, pos = p_binop_operator(s)
n2 = p_sub_expr(s)
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
#expression: or_test [if or_test else test] | lambda_form
......
from __future__ import division
__doc__ = u"""
>>> from future_division import doit
>>> doit(1,2)
(0.5, 0)
>>> doit(4,3)
......@@ -10,7 +9,55 @@ __doc__ = u"""
(1.3333333333333333, 1.0)
>>> doit(4,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):
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