Skip to content

Commit

Permalink
[expressions] Make int/int return double results.
Browse files Browse the repository at this point in the history
Also add a new "//" operator to perform integer division.
(fix #5153)
  • Loading branch information
nyalldawson committed Dec 6, 2014
1 parent 26e06e6 commit 232aaac
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 7 deletions.
1 change: 1 addition & 0 deletions python/core/qgsexpression.sip
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class QgsExpression
boMinus,
boMul,
boDiv,
boIntDiv,
boMod,
boPow,

Expand Down
17 changes: 13 additions & 4 deletions src/core/qgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

#include <QtDebug>
#include <QDomDocument>
#include <QSettings>
#include <QDate>
#include <QRegExp>
#include <QColor>
Expand Down Expand Up @@ -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 ) )
Expand All @@ -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();
Expand Down Expand Up @@ -2421,6 +2429,7 @@ int QgsExpression::NodeBinaryOperator::precedence() const

case boMul:
case boDiv:
case boIntDiv:
case boMod:
return 5;

Expand Down
1 change: 1 addition & 0 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class CORE_EXPORT QgsExpression
boMinus,
boMul,
boDiv,
boIntDiv,
boMod,
boPow,

Expand Down
1 change: 1 addition & 0 deletions src/core/qgsexpressionlexer.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
5 changes: 3 additions & 2 deletions src/core/qgsexpressionparser.yy
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ struct expression_parser_context
//

// operator tokens
%token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV MOD CONCAT POW
%token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV INTDIV MOD CONCAT POW
%token <u_op> NOT
%token IN

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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); }
Expand Down
8 changes: 7 additions & 1 deletion tests/src/core/testqgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand All @@ -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 );
Expand Down

0 comments on commit 232aaac

Please sign in to comment.