From 9520d3dfa4d3213ef07894dd99d201aeab255fa3 Mon Sep 17 00:00:00 2001 From: Krishna Veera Reddy Date: Sun, 15 Dec 2019 19:26:44 -0800 Subject: [PATCH] Suggest usage of `powi` method when applicable --- clippy_lints/src/floating_point_arithmetic.rs | 41 +++++++++++++++++- tests/ui/floating_point_arithmetic.rs | 12 ++++++ tests/ui/floating_point_arithmetic.stderr | 42 +++++++++++++++---- 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 da55f1e5f4e3..84a9cac5b634 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) { } } +// 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 { + 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) { // 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) { } 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 1feeb827621a..77a14a7a6668 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 a7db1669745a..0fc05bce2528 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