about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs41
-rw-r--r--tests/ui/floating_point_arithmetic.rs12
-rw-r--r--tests/ui/floating_point_arithmetic.stderr42
3 files changed, 85 insertions, 10 deletions
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index da55f1e5f4e..84a9cac5b63 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -1,5 +1,5 @@
 use crate::consts::{
-    constant,
+    constant, Constant,
     Constant::{F32, F64},
 };
 use crate::utils::*;
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     /// let _ = a.log(E);
     /// let _ = (1.0 + a).ln();
     /// let _ = a.exp() - 1.0;
+    /// let _ = a.powf(2.0);
     /// ```
     ///
     /// is better expressed as
@@ -54,6 +55,7 @@ declare_clippy_lint! {
     /// let _ = a.ln();
     /// let _ = a.ln_1p();
     /// let _ = a.exp_m1();
+    /// let _ = a.powi(2);
     /// ```
     pub FLOATING_POINT_IMPROVEMENTS,
     nursery,
@@ -114,6 +116,31 @@ fn check_ln1p(cx: &LateContext<'_, '_>, expr: &Expr, args: &HirVec<Expr>) {
     }
 }
 
+// Returns an integer if the float constant is a whole number and it
+// can be converted to an integer without loss
+// TODO: Add a better check to determine whether the float can be
+// casted without loss
+#[allow(clippy::cast_possible_truncation)]
+fn get_integer_from_float_constant(value: &Constant) -> Option<i64> {
+    match value {
+        F32(num) if (num.trunc() - num).abs() <= std::f32::EPSILON => {
+            if *num > -16_777_217.0 && *num < 16_777_217.0 {
+                Some(num.round() as i64)
+            } else {
+                None
+            }
+        },
+        F64(num) if (num.trunc() - num).abs() <= std::f64::EPSILON => {
+            if *num > -9_007_199_254_740_993.0 && *num < 9_007_199_254_740_993.0 {
+                Some(num.round() as i64)
+            } else {
+                None
+            }
+        },
+        _ => None,
+    }
+}
+
 fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr, args: &HirVec<Expr>) {
     // Check receiver
     if let Some((value, _)) = constant(cx, cx.tables, &args[0]) {
@@ -149,6 +176,18 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr, args: &HirVec<Expr>) {
         } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
             help = "cube-root of a number can be computed more accurately";
             method = "cbrt";
+        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
+            span_lint_and_sugg(
+                cx,
+                FLOATING_POINT_IMPROVEMENTS,
+                expr.span,
+                "exponentiation with integer powers can be computed more efficiently",
+                "consider using",
+                format!("{}.powi({})", sugg::Sugg::hir(cx, &args[0], ".."), exponent),
+                Applicability::MachineApplicable,
+            );
+
+            return;
         } else {
             return;
         }
diff --git a/tests/ui/floating_point_arithmetic.rs b/tests/ui/floating_point_arithmetic.rs
index 1feeb827621..77a14a7a666 100644
--- a/tests/ui/floating_point_arithmetic.rs
+++ b/tests/ui/floating_point_arithmetic.rs
@@ -46,12 +46,24 @@ fn check_powf() {
     let _ = std::f32::consts::E.powf(x);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
+    let _ = x.powf(2.0);
+    let _ = x.powf(-2.0);
+    let _ = x.powf(2.1);
+    let _ = x.powf(-2.1);
+    let _ = x.powf(16_777_217.0);
+    let _ = x.powf(-16_777_217.0);
 
     let x = 3f64;
     let _ = 2f64.powf(x);
     let _ = std::f64::consts::E.powf(x);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
+    let _ = x.powf(2.0);
+    let _ = x.powf(-2.0);
+    let _ = x.powf(2.1);
+    let _ = x.powf(-2.1);
+    let _ = x.powf(9_007_199_254_740_993.0);
+    let _ = x.powf(-9_007_199_254_740_993.0);
 }
 
 fn check_expm1() {
diff --git a/tests/ui/floating_point_arithmetic.stderr b/tests/ui/floating_point_arithmetic.stderr
index a7db1669745..0fc05bce252 100644
--- a/tests/ui/floating_point_arithmetic.stderr
+++ b/tests/ui/floating_point_arithmetic.stderr
@@ -120,53 +120,77 @@ error: cube-root of a number can be computed more accurately
 LL |     let _ = x.powf(1.0 / 3.0);
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_arithmetic.rs:49:13
+   |
+LL |     let _ = x.powf(2.0);
+   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_arithmetic.rs:50:13
+   |
+LL |     let _ = x.powf(-2.0);
+   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
+
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:51:13
+  --> $DIR/floating_point_arithmetic.rs:57:13
    |
 LL |     let _ = 2f64.powf(x);
    |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
 
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:52:13
+  --> $DIR/floating_point_arithmetic.rs:58:13
    |
 LL |     let _ = std::f64::consts::E.powf(x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
 
 error: square-root of a number can be computed more efficiently and accurately
-  --> $DIR/floating_point_arithmetic.rs:53:13
+  --> $DIR/floating_point_arithmetic.rs:59:13
    |
 LL |     let _ = x.powf(1.0 / 2.0);
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
 
 error: cube-root of a number can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:54:13
+  --> $DIR/floating_point_arithmetic.rs:60:13
    |
 LL |     let _ = x.powf(1.0 / 3.0);
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_arithmetic.rs:61:13
+   |
+LL |     let _ = x.powf(2.0);
+   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_arithmetic.rs:62:13
+   |
+LL |     let _ = x.powf(-2.0);
+   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
+
 error: (e.pow(x) - 1) can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:59:13
+  --> $DIR/floating_point_arithmetic.rs:71:13
    |
 LL |     let _ = x.exp() - 1.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
 error: (e.pow(x) - 1) can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:60:13
+  --> $DIR/floating_point_arithmetic.rs:72:13
    |
 LL |     let _ = x.exp() - 1.0 + 2.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
 error: (e.pow(x) - 1) can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:66:13
+  --> $DIR/floating_point_arithmetic.rs:78:13
    |
 LL |     let _ = x.exp() - 1.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
 error: (e.pow(x) - 1) can be computed more accurately
-  --> $DIR/floating_point_arithmetic.rs:67:13
+  --> $DIR/floating_point_arithmetic.rs:79:13
    |
 LL |     let _ = x.exp() - 1.0 + 2.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
-error: aborting due to 28 previous errors
+error: aborting due to 32 previous errors