Browse Source

Numerical overflow should result in `null` and a warning

To implement this we:

* Cast both arguments to double
* Perform integer and long validation on the double results before casting back to integer or long
* Perform a special case validation for exponent==1
* Any validation failures result in ArithmeticException, which is caught and added to warnings
Craig Taverner 2 years ago
parent
commit
75ea3ab3cd

+ 32 - 2
docs/reference/esql/functions/pow.asciidoc

@@ -12,6 +12,8 @@ include::{esql-specs}/math.csv-spec[tag=powDI]
 include::{esql-specs}/math.csv-spec[tag=powDI-result]
 |===
 
+==== Type rules
+
 The type of the returned value is determined by the types of the base and exponent.
 The following rules are applied to determine the result type:
 
@@ -30,7 +32,15 @@ include::{esql-specs}/math.csv-spec[tag=powII]
 include::{esql-specs}/math.csv-spec[tag=powII-result]
 |===
 
-Numeric overruns do not result in an error. For example:
+Note: The actual power function is performed using double precision values for all cases.
+This means that for very large non-floating point values can lead to very slightly different answers.
+However, a more likely outcome of very large non-floating point values is numerical overflow.
+
+==== Arithmetic errors
+
+Arithmetic errors and numeric overflow do not result in an error, instead the result will be `null`
+and a warning for the `ArithmeticException` added.
+For example:
 
 [source.merge.styled,esql]
 ----
@@ -38,10 +48,14 @@ include::{esql-specs}/math.csv-spec[tag=powULOverrun]
 ----
 [%header.monospaced.styled,format=dsv,separator=|]
 |===
+include::{esql-specs}/math.csv-spec[tag=powULOverrun-warning]
+|===
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
 include::{esql-specs}/math.csv-spec[tag=powULOverrun-result]
 |===
 
-If it is desired to protect against numeric overruns, use `to_double` on any one of the arguments:
+If it is desired to protect against numerical overruns, use `to_double` on any one of the arguments:
 
 [source.merge.styled,esql]
 ----
@@ -52,6 +66,22 @@ include::{esql-specs}/math.csv-spec[tag=pow2d]
 include::{esql-specs}/math.csv-spec[tag=pow2d-result]
 |===
 
+==== Fractional exponents
+
+The exponent can be a fraction, which is similar to performing a root.
+For example, the exponent of `0.5` will give the square root of the base:
+
+[source.merge.styled,esql]
+----
+include::{esql-specs}/math.csv-spec[tag=powID-sqrt]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/math.csv-spec[tag=powID-sqrt-result]
+|===
+
+==== Table of supported input and output types
+
 For clarity, the following table describes the output result type for all combinations of numeric input types:
 
 [cols="1,1,1"]

+ 46 - 8
x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec

@@ -116,6 +116,8 @@ height:double | s:double
 
 powSalarySquared
 from employees | eval s = pow(salary - 75000, 2) + 10000 | keep salary, s | sort salary desc | limit 4;
+warning:Line 1:27: evaluation of [pow(salary - 75000, 2)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: integer overflow
 
 salary:integer | s:integer
 74999          | 10001
@@ -230,6 +232,34 @@ base:double | exponent:double | s:double
 2.0         | 2.0             | 4.0
 ;
 
+powIntDouble
+// tag::powID-sqrt[]
+ROW base = 4, exponent = 0.5
+| EVAL s = POW(base, exponent)
+// end::powID-sqrt[]
+;
+
+// tag::powID-sqrt-result[]
+base:integer | exponent:double | s:double
+4            | 0.5             | 2.0
+// end::powID-sqrt-result[]
+;
+
+powSqrtNeg
+// tag::powNeg-sqrt[]
+ROW base = -4, exponent = 0.5
+| EVAL s = POW(base, exponent)
+// end::powNeg-sqrt[]
+;
+warning:Line 2:12: evaluation of [POW(base, exponent)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: invalid result: pow(-4.0, 0.5)
+
+// tag::powNeg-sqrt-result[]
+base:integer | exponent:double | s:double
+-4           | 0.5             | null
+// end::powNeg-sqrt-result[]
+;
+
 powDoubleInt
 // tag::powDI[]
 ROW base = 2.0, exponent = 2
@@ -300,13 +330,15 @@ x:double
 
 powIntULOverrun
 row x = pow(2, 9223372036854775808);
+warning:Line 1:9: evaluation of [pow(2, 9223372036854775808)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: long overflow
 
 x:long
-1
+null
 ;
 
 powULInt
-row x = pow(9223372036854775808, -10);
+row x = pow(to_unsigned_long(9223372036854775807), 1);
 
 x:long
 9223372036854775807
@@ -314,13 +346,17 @@ x:long
 
 powULIntOverrun
 // tag::powULOverrun[]
-ROW x = POW(9223372036854775808, 1)
+ROW x = POW(9223372036854775808, 2)
 // end::powULOverrun[]
 ;
+// tag::powULOverrun-warning[]
+warning:Line 1:9: evaluation of [POW(9223372036854775808, 2)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: long overflow
+// end::powULOverrun-warning[]
 
 // tag::powULOverrun-result[]
 x:long
-0
+null
 // end::powULOverrun-result[]
 ;
 
@@ -337,17 +373,19 @@ x:double
 ;
 
 powULLong
-row x = to_long(-10) | eval x = pow(9223372036854775808, x);
+row x = to_long(10) | eval x = pow(to_unsigned_long(10), x);
 
 x:long
-9223372036854775807
+10000000000
 ;
 
 powULLongOverrun
-row x = to_long(1) | eval x = pow(9223372036854775808, x);
+row x = to_long(100) | eval x = pow(to_unsigned_long(10), x);
+warning:Line 1:33: evaluation of [pow(to_unsigned_long(10), x)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: long overflow
 
 x:long
-0
+null
 ;
 
 powULDouble

+ 22 - 7
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java

@@ -4,6 +4,7 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
@@ -11,18 +12,23 @@ import org.elasticsearch.compute.data.DoubleBlock;
 import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Pow}.
  * This class is generated. Do not edit it.
  */
 public final class PowDoubleEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator base;
 
   private final EvalOperator.ExpressionEvaluator exponent;
 
-  public PowDoubleEvaluator(EvalOperator.ExpressionEvaluator base,
+  public PowDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator base,
       EvalOperator.ExpressionEvaluator exponent) {
+    this.warnings = new Warnings(source);
     this.base = base;
     this.exponent = exponent;
   }
@@ -47,7 +53,7 @@ public final class PowDoubleEvaluator implements EvalOperator.ExpressionEvaluato
     if (exponentVector == null) {
       return eval(page.getPositionCount(), baseBlock, exponentBlock);
     }
-    return eval(page.getPositionCount(), baseVector, exponentVector).asBlock();
+    return eval(page.getPositionCount(), baseVector, exponentVector);
   }
 
   public DoubleBlock eval(int positionCount, DoubleBlock baseBlock, DoubleBlock exponentBlock) {
@@ -61,16 +67,25 @@ public final class PowDoubleEvaluator implements EvalOperator.ExpressionEvaluato
         result.appendNull();
         continue position;
       }
-      result.appendDouble(Pow.process(baseBlock.getDouble(baseBlock.getFirstValueIndex(p)), exponentBlock.getDouble(exponentBlock.getFirstValueIndex(p))));
+      try {
+        result.appendDouble(Pow.process(baseBlock.getDouble(baseBlock.getFirstValueIndex(p)), exponentBlock.getDouble(exponentBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public DoubleVector eval(int positionCount, DoubleVector baseVector,
-      DoubleVector exponentVector) {
-    DoubleVector.Builder result = DoubleVector.newVectorBuilder(positionCount);
+  public DoubleBlock eval(int positionCount, DoubleVector baseVector, DoubleVector exponentVector) {
+    DoubleBlock.Builder result = DoubleBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendDouble(Pow.process(baseVector.getDouble(p), exponentVector.getDouble(p)));
+      try {
+        result.appendDouble(Pow.process(baseVector.getDouble(p), exponentVector.getDouble(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 29 - 12
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java

@@ -4,25 +4,32 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.DoubleBlock;
+import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.IntBlock;
-import org.elasticsearch.compute.data.IntVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Pow}.
  * This class is generated. Do not edit it.
  */
 public final class PowIntEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator base;
 
   private final EvalOperator.ExpressionEvaluator exponent;
 
-  public PowIntEvaluator(EvalOperator.ExpressionEvaluator base,
+  public PowIntEvaluator(Source source, EvalOperator.ExpressionEvaluator base,
       EvalOperator.ExpressionEvaluator exponent) {
+    this.warnings = new Warnings(source);
     this.base = base;
     this.exponent = exponent;
   }
@@ -33,24 +40,24 @@ public final class PowIntEvaluator implements EvalOperator.ExpressionEvaluator {
     if (baseUncastBlock.areAllValuesNull()) {
       return Block.constantNullBlock(page.getPositionCount());
     }
-    IntBlock baseBlock = (IntBlock) baseUncastBlock;
+    DoubleBlock baseBlock = (DoubleBlock) baseUncastBlock;
     Block exponentUncastBlock = exponent.eval(page);
     if (exponentUncastBlock.areAllValuesNull()) {
       return Block.constantNullBlock(page.getPositionCount());
     }
-    IntBlock exponentBlock = (IntBlock) exponentUncastBlock;
-    IntVector baseVector = baseBlock.asVector();
+    DoubleBlock exponentBlock = (DoubleBlock) exponentUncastBlock;
+    DoubleVector baseVector = baseBlock.asVector();
     if (baseVector == null) {
       return eval(page.getPositionCount(), baseBlock, exponentBlock);
     }
-    IntVector exponentVector = exponentBlock.asVector();
+    DoubleVector exponentVector = exponentBlock.asVector();
     if (exponentVector == null) {
       return eval(page.getPositionCount(), baseBlock, exponentBlock);
     }
-    return eval(page.getPositionCount(), baseVector, exponentVector).asBlock();
+    return eval(page.getPositionCount(), baseVector, exponentVector);
   }
 
-  public IntBlock eval(int positionCount, IntBlock baseBlock, IntBlock exponentBlock) {
+  public IntBlock eval(int positionCount, DoubleBlock baseBlock, DoubleBlock exponentBlock) {
     IntBlock.Builder result = IntBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
       if (baseBlock.isNull(p) || baseBlock.getValueCount(p) != 1) {
@@ -61,15 +68,25 @@ public final class PowIntEvaluator implements EvalOperator.ExpressionEvaluator {
         result.appendNull();
         continue position;
       }
-      result.appendInt(Pow.process(baseBlock.getInt(baseBlock.getFirstValueIndex(p)), exponentBlock.getInt(exponentBlock.getFirstValueIndex(p))));
+      try {
+        result.appendInt(Pow.processInt(baseBlock.getDouble(baseBlock.getFirstValueIndex(p)), exponentBlock.getDouble(exponentBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public IntVector eval(int positionCount, IntVector baseVector, IntVector exponentVector) {
-    IntVector.Builder result = IntVector.newVectorBuilder(positionCount);
+  public IntBlock eval(int positionCount, DoubleVector baseVector, DoubleVector exponentVector) {
+    IntBlock.Builder result = IntBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendInt(Pow.process(baseVector.getInt(p), exponentVector.getInt(p)));
+      try {
+        result.appendInt(Pow.processInt(baseVector.getDouble(p), exponentVector.getDouble(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 29 - 12
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java

@@ -4,25 +4,32 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.DoubleBlock;
+import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.LongBlock;
-import org.elasticsearch.compute.data.LongVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Pow}.
  * This class is generated. Do not edit it.
  */
 public final class PowLongEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator base;
 
   private final EvalOperator.ExpressionEvaluator exponent;
 
-  public PowLongEvaluator(EvalOperator.ExpressionEvaluator base,
+  public PowLongEvaluator(Source source, EvalOperator.ExpressionEvaluator base,
       EvalOperator.ExpressionEvaluator exponent) {
+    this.warnings = new Warnings(source);
     this.base = base;
     this.exponent = exponent;
   }
@@ -33,24 +40,24 @@ public final class PowLongEvaluator implements EvalOperator.ExpressionEvaluator
     if (baseUncastBlock.areAllValuesNull()) {
       return Block.constantNullBlock(page.getPositionCount());
     }
-    LongBlock baseBlock = (LongBlock) baseUncastBlock;
+    DoubleBlock baseBlock = (DoubleBlock) baseUncastBlock;
     Block exponentUncastBlock = exponent.eval(page);
     if (exponentUncastBlock.areAllValuesNull()) {
       return Block.constantNullBlock(page.getPositionCount());
     }
-    LongBlock exponentBlock = (LongBlock) exponentUncastBlock;
-    LongVector baseVector = baseBlock.asVector();
+    DoubleBlock exponentBlock = (DoubleBlock) exponentUncastBlock;
+    DoubleVector baseVector = baseBlock.asVector();
     if (baseVector == null) {
       return eval(page.getPositionCount(), baseBlock, exponentBlock);
     }
-    LongVector exponentVector = exponentBlock.asVector();
+    DoubleVector exponentVector = exponentBlock.asVector();
     if (exponentVector == null) {
       return eval(page.getPositionCount(), baseBlock, exponentBlock);
     }
-    return eval(page.getPositionCount(), baseVector, exponentVector).asBlock();
+    return eval(page.getPositionCount(), baseVector, exponentVector);
   }
 
-  public LongBlock eval(int positionCount, LongBlock baseBlock, LongBlock exponentBlock) {
+  public LongBlock eval(int positionCount, DoubleBlock baseBlock, DoubleBlock exponentBlock) {
     LongBlock.Builder result = LongBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
       if (baseBlock.isNull(p) || baseBlock.getValueCount(p) != 1) {
@@ -61,15 +68,25 @@ public final class PowLongEvaluator implements EvalOperator.ExpressionEvaluator
         result.appendNull();
         continue position;
       }
-      result.appendLong(Pow.process(baseBlock.getLong(baseBlock.getFirstValueIndex(p)), exponentBlock.getLong(exponentBlock.getFirstValueIndex(p))));
+      try {
+        result.appendLong(Pow.processLong(baseBlock.getDouble(baseBlock.getFirstValueIndex(p)), exponentBlock.getDouble(exponentBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public LongVector eval(int positionCount, LongVector baseVector, LongVector exponentVector) {
-    LongVector.Builder result = LongVector.newVectorBuilder(positionCount);
+  public LongBlock eval(int positionCount, DoubleVector baseVector, DoubleVector exponentVector) {
+    LongBlock.Builder result = LongBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendLong(Pow.process(baseVector.getLong(p), exponentVector.getLong(p)));
+      try {
+        result.appendLong(Pow.processLong(baseVector.getDouble(p), exponentVector.getDouble(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 0 - 10
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java

@@ -15,8 +15,6 @@ import org.elasticsearch.xpack.ql.type.DataTypes;
 
 import java.util.function.Supplier;
 
-import static org.elasticsearch.xpack.ql.util.NumericUtils.asLongUnsigned;
-import static org.elasticsearch.xpack.ql.util.NumericUtils.unsignedLongAsBigInteger;
 import static org.elasticsearch.xpack.ql.util.NumericUtils.unsignedLongToDouble;
 
 public class Cast {
@@ -55,9 +53,6 @@ public class Cast {
             if (current == DataTypes.INTEGER) {
                 return () -> new CastIntToLongEvaluator(in.get());
             }
-            if (current == DataTypes.UNSIGNED_LONG) {
-                return () -> new CastUnsignedLongToLongEvaluator(in.get());
-            }
             throw cantCast(current, required);
         }
         throw cantCast(current, required);
@@ -87,11 +82,6 @@ public class Cast {
         return unsignedLongToDouble(v);
     }
 
-    @Evaluator(extraName = "UnsignedLongToLong")
-    static long castUnsignedLongToLong(long v) {
-        return asLongUnsigned(unsignedLongAsBigInteger(v));
-    }
-
     @Evaluator(extraName = "IntToUnsignedLong")
     static long castIntToUnsignedLong(int v) {
         return castLongToUnsignedLong(v);

+ 65 - 12
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Pow.java

@@ -66,19 +66,69 @@ public class Pow extends ScalarFunction implements OptionalArgument, Mappable {
         return Mappable.super.fold();
     }
 
-    @Evaluator(extraName = "Double")
+    @Evaluator(extraName = "Double", warnExceptions = { ArithmeticException.class })
     static double process(double base, double exponent) {
-        return Math.pow(base, exponent);
+        return validateAsDouble(base, exponent);
     }
 
-    @Evaluator(extraName = "Long")
-    static long process(long base, long exponent) {
-        return (long) Math.pow(base, exponent);
+    @Evaluator(extraName = "Long", warnExceptions = { ArithmeticException.class })
+    static long processLong(double base, double exponent) {
+        if (exponent == 1) {
+            return validateAsLong(base);
+        }
+        return validateAsLong(base, exponent);
+    }
+
+    @Evaluator(extraName = "Int", warnExceptions = { ArithmeticException.class })
+    static int processInt(double base, double exponent) {
+        if (exponent == 1) {
+            return validateAsInt(base);
+        }
+        return validateAsInt(base, exponent);
+    }
+
+    private static double validateAsDouble(double base, double exponent) {
+        double result = Math.pow(base, exponent);
+        if (Double.isNaN(result)) {
+            throw new ArithmeticException("invalid result: pow(" + base + ", " + exponent + ")");
+        }
+        return result;
+    }
+
+    private static long validateAsLong(double base, double exponent) {
+        double result = Math.pow(base, exponent);
+        if (Double.isNaN(result)) {
+            throw new ArithmeticException("invalid result: pow(" + base + ", " + exponent + ")");
+        }
+        return validateAsLong(result);
     }
 
-    @Evaluator(extraName = "Int")
-    static int process(int base, int exponent) {
-        return (int) Math.pow(base, exponent);
+    private static long validateAsLong(double value) {
+        if (Double.compare(value, Long.MAX_VALUE) > 0) {
+            throw new ArithmeticException("long overflow");
+        }
+        if (Double.compare(value, Long.MIN_VALUE) < 0) {
+            throw new ArithmeticException("long overflow");
+        }
+        return (long) value;
+    }
+
+    private static int validateAsInt(double base, double exponent) {
+        double result = Math.pow(base, exponent);
+        if (Double.isNaN(result)) {
+            throw new ArithmeticException("invalid result: pow(" + base + ", " + exponent + ")");
+        }
+        return validateAsInt(result);
+    }
+
+    private static int validateAsInt(double value) {
+        if (Double.compare(value, Integer.MAX_VALUE) > 0) {
+            throw new ArithmeticException("integer overflow");
+        }
+        if (Double.compare(value, Integer.MIN_VALUE) < 0) {
+            throw new ArithmeticException("integer overflow");
+        }
+        return (int) value;
     }
 
     @Override
@@ -127,18 +177,21 @@ public class Pow extends ScalarFunction implements OptionalArgument, Mappable {
         var exponentEvaluator = toEvaluator.apply(exponent);
         if (dataType == DataTypes.DOUBLE) {
             return () -> new PowDoubleEvaluator(
+                source(),
                 cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator).get(),
                 cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator).get()
             );
         } else if (dataType == DataTypes.LONG) {
             return () -> new PowLongEvaluator(
-                cast(base.dataType(), DataTypes.LONG, baseEvaluator).get(),
-                cast(exponent.dataType(), DataTypes.LONG, exponentEvaluator).get()
+                source(),
+                cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator).get(),
+                cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator).get()
             );
         } else {
             return () -> new PowIntEvaluator(
-                cast(base.dataType(), DataTypes.INTEGER, baseEvaluator).get(),
-                cast(exponent.dataType(), DataTypes.INTEGER, exponentEvaluator).get()
+                source(),
+                cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator).get(),
+                cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator).get()
             );
         }
     }