diff --git a/python/core/qgsexpression.sip b/python/core/qgsexpression.sip index 39e41fb1791..3847160fb72 100644 --- a/python/core/qgsexpression.sip +++ b/python/core/qgsexpression.sip @@ -150,6 +150,7 @@ class QgsExpression boMinus, boMul, boDiv, + boIntDiv, boMod, boPow, diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 4993c092564..d26c389f256 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -2173,14 +2172,14 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, const Q case boMul: case boDiv: case boMod: + { if ( isNull( vL ) || isNull( vR ) ) return QVariant(); - else if ( isIntSafe( vL ) && isIntSafe( vR ) ) + else if ( mOp != boDiv && isIntSafe( vL ) && isIntSafe( vR ) ) { // both are integers - let's use integer arithmetics int iL = getIntValue( vL, parent ); ENSURE_NO_EVAL_ERROR; int iR = getIntValue( vR, parent ); ENSURE_NO_EVAL_ERROR; - if ( mOp == boDiv && iR == 0 ) return QVariant(); // silently handle division by zero and return NULL return QVariant( computeInt( iL, iR ) ); } else if ( isDateTimeSafe( vL ) && isIntervalSafe( vR ) ) @@ -2203,7 +2202,16 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, const Q return QVariant(); // silently handle division by zero and return NULL return QVariant( computeDouble( fL, fR ) ); } - + } + case boIntDiv: + { + //integer division + double fL = getDoubleValue( vL, parent ); ENSURE_NO_EVAL_ERROR; + double fR = getDoubleValue( vR, parent ); ENSURE_NO_EVAL_ERROR; + if ( fR == 0 ) + return QVariant(); // silently handle division by zero and return NULL + return QVariant( qFloor( fL / fR ) ); + } case boPow: if ( isNull( vL ) || isNull( vR ) ) return QVariant(); @@ -2421,6 +2429,7 @@ int QgsExpression::NodeBinaryOperator::precedence() const case boMul: case boDiv: + case boIntDiv: case boMod: return 5; diff --git a/src/core/qgsexpression.h b/src/core/qgsexpression.h index 378d3ade3dd..7701d500e16 100644 --- a/src/core/qgsexpression.h +++ b/src/core/qgsexpression.h @@ -241,6 +241,7 @@ class CORE_EXPORT QgsExpression boMinus, boMul, boDiv, + boIntDiv, boMod, boPow, diff --git a/src/core/qgsexpressionlexer.ll b/src/core/qgsexpressionlexer.ll index 1afdad6883d..ab73c484fbe 100644 --- a/src/core/qgsexpressionlexer.ll +++ b/src/core/qgsexpressionlexer.ll @@ -155,6 +155,7 @@ string "'"{str_char}*"'" "+" { B_OP(boPlus); return PLUS; } "-" { B_OP(boMinus); return MINUS; } "*" { B_OP(boMul); return MUL; } +"//" { B_OP(boIntDiv); return INTDIV; } "/" { B_OP(boDiv); return DIV; } "%" { B_OP(boMod); return MOD; } "^" { B_OP(boPow); return POW; } diff --git a/src/core/qgsexpressionparser.yy b/src/core/qgsexpressionparser.yy index 458f9cc79b4..81ee4a7085a 100644 --- a/src/core/qgsexpressionparser.yy +++ b/src/core/qgsexpressionparser.yy @@ -94,7 +94,7 @@ struct expression_parser_context // // operator tokens -%token OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV MOD CONCAT POW +%token OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV INTDIV MOD CONCAT POW %token NOT %token IN @@ -136,7 +136,7 @@ struct expression_parser_context %right NOT %left EQ NE LE GE LT GT REGEXP LIKE IS IN %left PLUS MINUS -%left MUL DIV MOD +%left MUL DIV INTDIV MOD %right POW %left CONCAT @@ -168,6 +168,7 @@ expression: | expression PLUS expression { $$ = BINOP($2, $1, $3); } | expression MINUS expression { $$ = BINOP($2, $1, $3); } | expression MUL expression { $$ = BINOP($2, $1, $3); } + | expression INTDIV expression { $$ = BINOP($2, $1, $3); } | expression DIV expression { $$ = BINOP($2, $1, $3); } | expression MOD expression { $$ = BINOP($2, $1, $3); } | expression POW expression { $$ = BINOP($2, $1, $3); } diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 63398401b05..73a376a5c1f 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -172,7 +172,7 @@ class TestQgsExpression: public QObject QTest::newRow( "plus invalid" ) << "1+'foo'" << true << QVariant(); QTest::newRow( "minus int" ) << "1-3" << false << QVariant( -2 ); QTest::newRow( "mul int" ) << "8*7" << false << QVariant( 56 ); - QTest::newRow( "div int" ) << "20/6" << false << QVariant( 3 ); + QTest::newRow( "div int" ) << "5/2" << false << QVariant( 2.5 ); QTest::newRow( "mod int" ) << "20%6" << false << QVariant( 2 ); QTest::newRow( "minus double" ) << "5.2-3.1" << false << QVariant( 2.1 ); QTest::newRow( "mul double" ) << "2.1*5" << false << QVariant( 10.5 ); @@ -181,6 +181,12 @@ class TestQgsExpression: public QObject QTest::newRow( "pow" ) << "2^8" << false << QVariant( 256. ); QTest::newRow( "division by zero" ) << "1/0" << false << QVariant(); QTest::newRow( "division by zero" ) << "1.0/0.0" << false << QVariant(); + QTest::newRow( "int division" ) << "5//2" << false << QVariant( 2 ); + QTest::newRow( "int division with doubles" ) << "5.0//2.0" << false << QVariant( 2 ); + QTest::newRow( "negative int division" ) << "-5//2" << false << QVariant( -3 ); + QTest::newRow( "negative int division with doubles" ) << "-5.0//2.0" << false << QVariant( -3 ); + QTest::newRow( "int division by zero" ) << "1//0" << false << QVariant(); + QTest::newRow( "int division by zero with floats" ) << "1.0//0.0" << false << QVariant(); // comparison QTest::newRow( "eq int" ) << "1+1 = 2" << false << QVariant( 1 );