about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--README.md2
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs380
-rw-r--r--clippy_lints/src/lib.rs10
-rw-r--r--clippy_lints/src/mul_add.rs111
-rw-r--r--src/lintlist/mod.rs23
-rw-r--r--tests/ui/floating_point_exp.fixed18
-rw-r--r--tests/ui/floating_point_exp.rs18
-rw-r--r--tests/ui/floating_point_exp.stderr28
-rw-r--r--tests/ui/floating_point_log.fixed58
-rw-r--r--tests/ui/floating_point_log.rs58
-rw-r--r--tests/ui/floating_point_log.stderr174
-rw-r--r--tests/ui/floating_point_mul_add.fixed21
-rw-r--r--tests/ui/floating_point_mul_add.rs21
-rw-r--r--tests/ui/floating_point_mul_add.stderr58
-rw-r--r--tests/ui/floating_point_powf.fixed42
-rw-r--r--tests/ui/floating_point_powf.rs42
-rw-r--r--tests/ui/floating_point_powf.stderr150
-rw-r--r--tests/ui/mul_add.rs16
-rw-r--r--tests/ui/mul_add.stderr34
-rw-r--r--tests/ui/mul_add_fixable.fixed24
-rw-r--r--tests/ui/mul_add_fixable.rs24
-rw-r--r--tests/ui/mul_add_fixable.stderr40
23 files changed, 1092 insertions, 263 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e99342e4a4..99e84ea5193 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1169,6 +1169,7 @@ Released 2018-09-13
 [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
+[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
@@ -1210,7 +1211,6 @@ Released 2018-09-13
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
-[`manual_mul_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_mul_add
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
@@ -1349,6 +1349,7 @@ Released 2018-09-13
 [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
+[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 [`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 [`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
diff --git a/README.md b/README.md
index 3103f960839..1300c5ad47b 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are 357 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are 358 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
 
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
new file mode 100644
index 00000000000..eed4f58cf90
--- /dev/null
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -0,0 +1,380 @@
+use crate::consts::{
+    constant, Constant,
+    Constant::{F32, F64},
+};
+use crate::utils::{span_lint_and_sugg, sugg};
+use if_chain::if_chain;
+use rustc::ty;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+
+use std::f32::consts as f32_consts;
+use std::f64::consts as f64_consts;
+use sugg::{format_numeric_literal, Sugg};
+use syntax::ast;
+
+declare_clippy_lint! {
+    /// **What it does:** Looks for floating-point expressions that
+    /// can be expressed using built-in methods to improve accuracy
+    /// at the cost of performance.
+    ///
+    /// **Why is this bad?** Negatively impacts accuracy.
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    ///
+    /// let a = 3f32;
+    /// let _ = a.powf(1.0 / 3.0);
+    /// let _ = (1.0 + a).ln();
+    /// let _ = a.exp() - 1.0;
+    /// ```
+    ///
+    /// is better expressed as
+    ///
+    /// ```rust
+    ///
+    /// let a = 3f32;
+    /// let _ = a.cbrt();
+    /// let _ = a.ln_1p();
+    /// let _ = a.exp_m1();
+    /// ```
+    pub IMPRECISE_FLOPS,
+    nursery,
+    "usage of imprecise floating point operations"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Looks for floating-point expressions that
+    /// can be expressed using built-in methods to improve both
+    /// accuracy and performance.
+    ///
+    /// **Why is this bad?** Negatively impacts accuracy and performance.
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// use std::f32::consts::E;
+    ///
+    /// let a = 3f32;
+    /// let _ = (2f32).powf(a);
+    /// let _ = E.powf(a);
+    /// let _ = a.powf(1.0 / 2.0);
+    /// let _ = a.log(2.0);
+    /// let _ = a.log(10.0);
+    /// let _ = a.log(E);
+    /// let _ = a.powf(2.0);
+    /// let _ = a * 2.0 + 4.0;
+    /// ```
+    ///
+    /// is better expressed as
+    ///
+    /// ```rust
+    /// use std::f32::consts::E;
+    ///
+    /// let a = 3f32;
+    /// let _ = a.exp2();
+    /// let _ = a.exp();
+    /// let _ = a.sqrt();
+    /// let _ = a.log2();
+    /// let _ = a.log10();
+    /// let _ = a.ln();
+    /// let _ = a.powi(2);
+    /// let _ = a.mul_add(2.0, 4.0);
+    /// ```
+    pub SUBOPTIMAL_FLOPS,
+    nursery,
+    "usage of sub-optimal floating point operations"
+}
+
+declare_lint_pass!(FloatingPointArithmetic => [
+    IMPRECISE_FLOPS,
+    SUBOPTIMAL_FLOPS
+]);
+
+// Returns the specialized log method for a given base if base is constant
+// and is one of 2, 10 and e
+fn get_specialized_log_method(cx: &LateContext<'_, '_>, base: &Expr<'_>) -> Option<&'static str> {
+    if let Some((value, _)) = constant(cx, cx.tables, base) {
+        if F32(2.0) == value || F64(2.0) == value {
+            return Some("log2");
+        } else if F32(10.0) == value || F64(10.0) == value {
+            return Some("log10");
+        } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
+            return Some("ln");
+        }
+    }
+
+    None
+}
+
+// Adds type suffixes and parenthesis to method receivers if necessary
+fn prepare_receiver_sugg<'a>(cx: &LateContext<'_, '_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
+    let mut suggestion = Sugg::hir(cx, expr, "..");
+
+    if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind {
+        expr = &inner_expr;
+    }
+
+    if_chain! {
+        // if the expression is a float literal and it is unsuffixed then
+        // add a suffix so the suggestion is valid and unambiguous
+        if let ty::Float(float_ty) = cx.tables.expr_ty(expr).kind;
+        if let ExprKind::Lit(lit) = &expr.kind;
+        if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
+        then {
+            let op = format!(
+                "{}{}{}",
+                suggestion,
+                // Check for float literals without numbers following the decimal
+                // separator such as `2.` and adds a trailing zero
+                if sym.as_str().ends_with('.') {
+                    "0"
+                } else {
+                    ""
+                },
+                float_ty.name_str()
+            ).into();
+
+            suggestion = match suggestion {
+                Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
+                _ => Sugg::NonParen(op)
+            };
+        }
+    }
+
+    suggestion.maybe_par()
+}
+
+fn check_log_base(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some(method) = get_specialized_log_method(cx, &args[1]) {
+        span_lint_and_sugg(
+            cx,
+            SUBOPTIMAL_FLOPS,
+            expr.span,
+            "logarithm for bases 2, 10 and e can be computed more accurately",
+            "consider using",
+            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
+// suggest usage of `(x + (y - 1)).ln_1p()` instead
+fn check_ln1p(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let ExprKind::Binary(
+        Spanned {
+            node: BinOpKind::Add, ..
+        },
+        lhs,
+        rhs,
+    ) = &args[0].kind
+    {
+        let recv = match (constant(cx, cx.tables, lhs), constant(cx, cx.tables, rhs)) {
+            (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
+            (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
+            _ => return,
+        };
+
+        span_lint_and_sugg(
+            cx,
+            IMPRECISE_FLOPS,
+            expr.span,
+            "ln(1 + x) can be computed more accurately",
+            "consider using",
+            format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+// Returns an integer if the float constant is a whole number and it can be
+// converted to an integer without loss of precision. For now we only check
+// ranges [-16777215, 16777216) for type f32 as whole number floats outside
+// this range are lossy and ambiguous.
+#[allow(clippy::cast_possible_truncation)]
+fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
+    match value {
+        F32(num) if num.fract() == 0.0 => {
+            if (-16_777_215.0..16_777_216.0).contains(num) {
+                Some(num.round() as i32)
+            } else {
+                None
+            }
+        },
+        F64(num) if num.fract() == 0.0 => {
+            if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
+                Some(num.round() as i32)
+            } else {
+                None
+            }
+        },
+        _ => None,
+    }
+}
+
+fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    // Check receiver
+    if let Some((value, _)) = constant(cx, cx.tables, &args[0]) {
+        let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
+            "exp"
+        } else if F32(2.0) == value || F64(2.0) == value {
+            "exp2"
+        } else {
+            return;
+        };
+
+        span_lint_and_sugg(
+            cx,
+            SUBOPTIMAL_FLOPS,
+            expr.span,
+            "exponent for bases 2 and e can be computed more accurately",
+            "consider using",
+            format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
+            Applicability::MachineApplicable,
+        );
+    }
+
+    // Check argument
+    if let Some((value, _)) = constant(cx, cx.tables, &args[1]) {
+        let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
+            (
+                SUBOPTIMAL_FLOPS,
+                "square-root of a number can be computed more efficiently and accurately",
+                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
+            )
+        } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
+            (
+                IMPRECISE_FLOPS,
+                "cube-root of a number can be computed more accurately",
+                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
+            )
+        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
+            (
+                SUBOPTIMAL_FLOPS,
+                "exponentiation with integer powers can be computed more efficiently",
+                format!(
+                    "{}.powi({})",
+                    Sugg::hir(cx, &args[0], ".."),
+                    format_numeric_literal(&exponent.to_string(), None, false)
+                ),
+            )
+        } else {
+            return;
+        };
+
+        span_lint_and_sugg(
+            cx,
+            lint,
+            expr.span,
+            help,
+            "consider using",
+            suggestion,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+// TODO: Lint expressions of the form `x.exp() - y` where y > 1
+// and suggest usage of `x.exp_m1() - (y - 1)` instead
+fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+    if_chain! {
+        if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind;
+        if cx.tables.expr_ty(lhs).is_floating_point();
+        if let Some((value, _)) = constant(cx, cx.tables, rhs);
+        if F32(1.0) == value || F64(1.0) == value;
+        if let ExprKind::MethodCall(ref path, _, ref method_args) = lhs.kind;
+        if cx.tables.expr_ty(&method_args[0]).is_floating_point();
+        if path.ident.name.as_str() == "exp";
+        then {
+            span_lint_and_sugg(
+                cx,
+                IMPRECISE_FLOPS,
+                expr.span,
+                "(e.pow(x) - 1) can be computed more accurately",
+                "consider using",
+                format!(
+                    "{}.exp_m1()",
+                    Sugg::hir(cx, &method_args[0], "..")
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn is_float_mul_expr<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
+    if_chain! {
+        if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind;
+        if cx.tables.expr_ty(lhs).is_floating_point();
+        if cx.tables.expr_ty(rhs).is_floating_point();
+        then {
+            return Some((lhs, rhs));
+        }
+    }
+
+    None
+}
+
+// TODO: Fix rust-lang/rust-clippy#4735
+fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+    if let ExprKind::Binary(
+        Spanned {
+            node: BinOpKind::Add, ..
+        },
+        lhs,
+        rhs,
+    ) = &expr.kind
+    {
+        let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
+            (inner_lhs, inner_rhs, rhs)
+        } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
+            (inner_lhs, inner_rhs, lhs)
+        } else {
+            return;
+        };
+
+        span_lint_and_sugg(
+            cx,
+            SUBOPTIMAL_FLOPS,
+            expr.span,
+            "multiply and add expressions can be calculated more efficiently and accurately",
+            "consider using",
+            format!(
+                "{}.mul_add({}, {})",
+                prepare_receiver_sugg(cx, recv),
+                Sugg::hir(cx, arg1, ".."),
+                Sugg::hir(cx, arg2, ".."),
+            ),
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
+    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::MethodCall(ref path, _, args) = &expr.kind {
+            let recv_ty = cx.tables.expr_ty(&args[0]);
+
+            if recv_ty.is_floating_point() {
+                match &*path.ident.name.as_str() {
+                    "ln" => check_ln1p(cx, expr, args),
+                    "log" => check_log_base(cx, expr, args),
+                    "powf" => check_powf(cx, expr, args),
+                    _ => {},
+                }
+            }
+        } else {
+            check_expm1(cx, expr);
+            check_mul_add(cx, expr);
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 2b5691f9255..c732657b2e5 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -208,6 +208,7 @@ pub mod exit;
 pub mod explicit_write;
 pub mod fallible_impl_from;
 pub mod float_literal;
+pub mod floating_point_arithmetic;
 pub mod format;
 pub mod formatting;
 pub mod functions;
@@ -248,7 +249,6 @@ pub mod missing_const_for_fn;
 pub mod missing_doc;
 pub mod missing_inline;
 pub mod modulo_arithmetic;
-pub mod mul_add;
 pub mod multiple_crate_versions;
 pub mod mut_key;
 pub mod mut_mut;
@@ -538,6 +538,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &fallible_impl_from::FALLIBLE_IMPL_FROM,
         &float_literal::EXCESSIVE_PRECISION,
         &float_literal::LOSSY_FLOAT_LITERAL,
+        &floating_point_arithmetic::IMPRECISE_FLOPS,
+        &floating_point_arithmetic::SUBOPTIMAL_FLOPS,
         &format::USELESS_FORMAT,
         &formatting::POSSIBLE_MISSING_COMMA,
         &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
@@ -690,7 +692,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
         &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
         &modulo_arithmetic::MODULO_ARITHMETIC,
-        &mul_add::MANUAL_MUL_ADD,
         &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
         &mut_key::MUTABLE_KEY_TYPE,
         &mut_mut::MUT_MUT,
@@ -966,7 +967,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box inherent_to_string::InherentToString);
     store.register_late_pass(|| box trait_bounds::TraitBounds);
     store.register_late_pass(|| box comparison_chain::ComparisonChain);
-    store.register_late_pass(|| box mul_add::MulAddCheck);
     store.register_late_pass(|| box mut_key::MutableKeyType);
     store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
     store.register_early_pass(|| box reference::DerefAddrOf);
@@ -1000,6 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
     let array_size_threshold = conf.array_size_threshold;
     store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
+    store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic);
     store.register_early_pass(|| box as_conversions::AsConversions);
     store.register_early_pass(|| box utils::internal_lints::ProduceIce);
     store.register_late_pass(|| box let_underscore::LetUnderscore);
@@ -1648,8 +1649,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
         LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
         LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
+        LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),
+        LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS),
         LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN),
-        LintId::of(&mul_add::MANUAL_MUL_ADD),
         LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
         LintId::of(&mutex_atomic::MUTEX_INTEGER),
         LintId::of(&needless_borrow::NEEDLESS_BORROW),
diff --git a/clippy_lints/src/mul_add.rs b/clippy_lints/src/mul_add.rs
deleted file mode 100644
index 57e56d8f959..00000000000
--- a/clippy_lints/src/mul_add.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-use crate::utils::{snippet, span_lint_and_sugg};
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for expressions of the form `a * b + c`
-    /// or `c + a * b` where `a`, `b`, `c` are floats and suggests using
-    /// `a.mul_add(b, c)` instead.
-    ///
-    /// **Why is this bad?** Calculating `a * b + c` may lead to slight
-    /// numerical inaccuracies as `a * b` is rounded before being added to
-    /// `c`. Depending on the target architecture, `mul_add()` may be more
-    /// performant.
-    ///
-    /// **Known problems:** This lint can emit semantic incorrect suggestions.
-    /// For example, for `a * b * c + d` the suggestion `a * b.mul_add(c, d)`
-    /// is emitted, which is equivalent to `a * (b * c + d)`. (#4735)
-    ///
-    /// **Example:**
-    ///
-    /// ```rust
-    /// # let a = 0_f32;
-    /// # let b = 0_f32;
-    /// # let c = 0_f32;
-    /// let foo = (a * b) + c;
-    /// ```
-    ///
-    /// can be written as
-    ///
-    /// ```rust
-    /// # let a = 0_f32;
-    /// # let b = 0_f32;
-    /// # let c = 0_f32;
-    /// let foo = a.mul_add(b, c);
-    /// ```
-    pub MANUAL_MUL_ADD,
-    nursery,
-    "Using `a.mul_add(b, c)` for floating points has higher numerical precision than `a * b + c`"
-}
-
-declare_lint_pass!(MulAddCheck => [MANUAL_MUL_ADD]);
-
-fn is_float<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
-    cx.tables.expr_ty(expr).is_floating_point()
-}
-
-// Checks whether expression is multiplication of two floats
-fn is_float_mult_expr<'a, 'tcx, 'b>(
-    cx: &LateContext<'a, 'tcx>,
-    expr: &'b Expr<'b>,
-) -> Option<(&'b Expr<'b>, &'b Expr<'b>)> {
-    if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
-        if let BinOpKind::Mul = op.node {
-            if is_float(cx, &lhs) && is_float(cx, &rhs) {
-                return Some((&lhs, &rhs));
-            }
-        }
-    }
-
-    None
-}
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MulAddCheck {
-    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
-            if let BinOpKind::Add = op.node {
-                //Converts mult_lhs * mult_rhs + rhs to mult_lhs.mult_add(mult_rhs, rhs)
-                if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, lhs) {
-                    if is_float(cx, rhs) {
-                        span_lint_and_sugg(
-                            cx,
-                            MANUAL_MUL_ADD,
-                            expr.span,
-                            "consider using `mul_add()` for better numerical precision",
-                            "try",
-                            format!(
-                                "{}.mul_add({}, {})",
-                                snippet(cx, mult_lhs.span, "_"),
-                                snippet(cx, mult_rhs.span, "_"),
-                                snippet(cx, rhs.span, "_"),
-                            ),
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-                //Converts lhs + mult_lhs * mult_rhs to mult_lhs.mult_add(mult_rhs, lhs)
-                if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, rhs) {
-                    if is_float(cx, lhs) {
-                        span_lint_and_sugg(
-                            cx,
-                            MANUAL_MUL_ADD,
-                            expr.span,
-                            "consider using `mul_add()` for better numerical precision",
-                            "try",
-                            format!(
-                                "{}.mul_add({}, {})",
-                                snippet(cx, mult_lhs.span, "_"),
-                                snippet(cx, mult_rhs.span, "_"),
-                                snippet(cx, lhs.span, "_"),
-                            ),
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index 29b5a7ba08a..15e6a4b6036 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -6,7 +6,7 @@ pub use lint::Lint;
 pub use lint::LINT_LEVELS;
 
 // begin lint list, do not remove this comment, it’s used in `update_lints`
-pub const ALL_LINTS: [Lint; 357] = [
+pub const ALL_LINTS: [Lint; 358] = [
     Lint {
         name: "absurd_extreme_comparisons",
         group: "correctness",
@@ -750,6 +750,13 @@ pub const ALL_LINTS: [Lint; 357] = [
         module: "implicit_return",
     },
     Lint {
+        name: "imprecise_flops",
+        group: "nursery",
+        desc: "usage of imprecise floating point operations",
+        deprecation: None,
+        module: "floating_point_arithmetic",
+    },
+    Lint {
         name: "inconsistent_digit_grouping",
         group: "style",
         desc: "integer literals with digits grouped inconsistently",
@@ -1023,13 +1030,6 @@ pub const ALL_LINTS: [Lint; 357] = [
         module: "loops",
     },
     Lint {
-        name: "manual_mul_add",
-        group: "nursery",
-        desc: "Using `a.mul_add(b, c)` for floating points has higher numerical precision than `a * b + c`",
-        deprecation: None,
-        module: "mul_add",
-    },
-    Lint {
         name: "manual_saturating_arithmetic",
         group: "style",
         desc: "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`",
@@ -1961,6 +1961,13 @@ pub const ALL_LINTS: [Lint; 357] = [
         module: "excessive_bools",
     },
     Lint {
+        name: "suboptimal_flops",
+        group: "nursery",
+        desc: "usage of sub-optimal floating point operations",
+        deprecation: None,
+        module: "floating_point_arithmetic",
+    },
+    Lint {
         name: "suspicious_arithmetic_impl",
         group: "correctness",
         desc: "suspicious use of operators in impl of arithmetic trait",
diff --git a/tests/ui/floating_point_exp.fixed b/tests/ui/floating_point_exp.fixed
new file mode 100644
index 00000000000..ae7805fdf01
--- /dev/null
+++ b/tests/ui/floating_point_exp.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+    let x = 2f32;
+    let _ = x.exp_m1();
+    let _ = x.exp_m1() + 2.0;
+    // Cases where the lint shouldn't be applied
+    let _ = x.exp() - 2.0;
+    let _ = x.exp() - 1.0 * 2.0;
+
+    let x = 2f64;
+    let _ = x.exp_m1();
+    let _ = x.exp_m1() + 2.0;
+    // Cases where the lint shouldn't be applied
+    let _ = x.exp() - 2.0;
+    let _ = x.exp() - 1.0 * 2.0;
+}
diff --git a/tests/ui/floating_point_exp.rs b/tests/ui/floating_point_exp.rs
new file mode 100644
index 00000000000..27e0b9bcbc9
--- /dev/null
+++ b/tests/ui/floating_point_exp.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+    let x = 2f32;
+    let _ = x.exp() - 1.0;
+    let _ = x.exp() - 1.0 + 2.0;
+    // Cases where the lint shouldn't be applied
+    let _ = x.exp() - 2.0;
+    let _ = x.exp() - 1.0 * 2.0;
+
+    let x = 2f64;
+    let _ = x.exp() - 1.0;
+    let _ = x.exp() - 1.0 + 2.0;
+    // Cases where the lint shouldn't be applied
+    let _ = x.exp() - 2.0;
+    let _ = x.exp() - 1.0 * 2.0;
+}
diff --git a/tests/ui/floating_point_exp.stderr b/tests/ui/floating_point_exp.stderr
new file mode 100644
index 00000000000..5cd999ad47c
--- /dev/null
+++ b/tests/ui/floating_point_exp.stderr
@@ -0,0 +1,28 @@
+error: (e.pow(x) - 1) can be computed more accurately
+  --> $DIR/floating_point_exp.rs:6:13
+   |
+LL |     let _ = x.exp() - 1.0;
+   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
+   |
+   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: (e.pow(x) - 1) can be computed more accurately
+  --> $DIR/floating_point_exp.rs:7: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_exp.rs:13: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_exp.rs:14:13
+   |
+LL |     let _ = x.exp() - 1.0 + 2.0;
+   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed
new file mode 100644
index 00000000000..42c5e5d2bae
--- /dev/null
+++ b/tests/ui/floating_point_log.fixed
@@ -0,0 +1,58 @@
+// run-rustfix
+#![allow(dead_code, clippy::double_parens)]
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+const TWO: f32 = 2.0;
+const E: f32 = std::f32::consts::E;
+
+fn check_log_base() {
+    let x = 1f32;
+    let _ = x.log2();
+    let _ = x.log10();
+    let _ = x.ln();
+    let _ = x.log2();
+    let _ = x.ln();
+
+    let x = 1f64;
+    let _ = x.log2();
+    let _ = x.log10();
+    let _ = x.ln();
+}
+
+fn check_ln1p() {
+    let x = 1f32;
+    let _ = 2.0f32.ln_1p();
+    let _ = 2.0f32.ln_1p();
+    let _ = x.ln_1p();
+    let _ = (x / 2.0).ln_1p();
+    let _ = x.powi(2).ln_1p();
+    let _ = (x.powi(2) / 2.0).ln_1p();
+    let _ = ((std::f32::consts::E - 1.0)).ln_1p();
+    let _ = x.ln_1p();
+    let _ = x.powi(2).ln_1p();
+    let _ = (x + 2.0).ln_1p();
+    let _ = (x / 2.0).ln_1p();
+    // Cases where the lint shouldn't be applied
+    let _ = (1.0 + x + 2.0).ln();
+    let _ = (x + 1.0 + 2.0).ln();
+    let _ = (x + 1.0 / 2.0).ln();
+    let _ = (1.0 + x - 2.0).ln();
+
+    let x = 1f64;
+    let _ = 2.0f64.ln_1p();
+    let _ = 2.0f64.ln_1p();
+    let _ = x.ln_1p();
+    let _ = (x / 2.0).ln_1p();
+    let _ = x.powi(2).ln_1p();
+    let _ = x.ln_1p();
+    let _ = x.powi(2).ln_1p();
+    let _ = (x + 2.0).ln_1p();
+    let _ = (x / 2.0).ln_1p();
+    // Cases where the lint shouldn't be applied
+    let _ = (1.0 + x + 2.0).ln();
+    let _ = (x + 1.0 + 2.0).ln();
+    let _ = (x + 1.0 / 2.0).ln();
+    let _ = (1.0 + x - 2.0).ln();
+}
+
+fn main() {}
diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs
new file mode 100644
index 00000000000..8be0d9ad56f
--- /dev/null
+++ b/tests/ui/floating_point_log.rs
@@ -0,0 +1,58 @@
+// run-rustfix
+#![allow(dead_code, clippy::double_parens)]
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+const TWO: f32 = 2.0;
+const E: f32 = std::f32::consts::E;
+
+fn check_log_base() {
+    let x = 1f32;
+    let _ = x.log(2f32);
+    let _ = x.log(10f32);
+    let _ = x.log(std::f32::consts::E);
+    let _ = x.log(TWO);
+    let _ = x.log(E);
+
+    let x = 1f64;
+    let _ = x.log(2f64);
+    let _ = x.log(10f64);
+    let _ = x.log(std::f64::consts::E);
+}
+
+fn check_ln1p() {
+    let x = 1f32;
+    let _ = (1f32 + 2.).ln();
+    let _ = (1f32 + 2.0).ln();
+    let _ = (1.0 + x).ln();
+    let _ = (1.0 + x / 2.0).ln();
+    let _ = (1.0 + x.powi(2)).ln();
+    let _ = (1.0 + x.powi(2) / 2.0).ln();
+    let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
+    let _ = (x + 1.0).ln();
+    let _ = (x.powi(2) + 1.0).ln();
+    let _ = (x + 2.0 + 1.0).ln();
+    let _ = (x / 2.0 + 1.0).ln();
+    // Cases where the lint shouldn't be applied
+    let _ = (1.0 + x + 2.0).ln();
+    let _ = (x + 1.0 + 2.0).ln();
+    let _ = (x + 1.0 / 2.0).ln();
+    let _ = (1.0 + x - 2.0).ln();
+
+    let x = 1f64;
+    let _ = (1f64 + 2.).ln();
+    let _ = (1f64 + 2.0).ln();
+    let _ = (1.0 + x).ln();
+    let _ = (1.0 + x / 2.0).ln();
+    let _ = (1.0 + x.powi(2)).ln();
+    let _ = (x + 1.0).ln();
+    let _ = (x.powi(2) + 1.0).ln();
+    let _ = (x + 2.0 + 1.0).ln();
+    let _ = (x / 2.0 + 1.0).ln();
+    // Cases where the lint shouldn't be applied
+    let _ = (1.0 + x + 2.0).ln();
+    let _ = (x + 1.0 + 2.0).ln();
+    let _ = (x + 1.0 / 2.0).ln();
+    let _ = (1.0 + x - 2.0).ln();
+}
+
+fn main() {}
diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr
new file mode 100644
index 00000000000..943fbdb0b83
--- /dev/null
+++ b/tests/ui/floating_point_log.stderr
@@ -0,0 +1,174 @@
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:10:13
+   |
+LL |     let _ = x.log(2f32);
+   |             ^^^^^^^^^^^ help: consider using: `x.log2()`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:11:13
+   |
+LL |     let _ = x.log(10f32);
+   |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:12:13
+   |
+LL |     let _ = x.log(std::f32::consts::E);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:13:13
+   |
+LL |     let _ = x.log(TWO);
+   |             ^^^^^^^^^^ help: consider using: `x.log2()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:14:13
+   |
+LL |     let _ = x.log(E);
+   |             ^^^^^^^^ help: consider using: `x.ln()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:17:13
+   |
+LL |     let _ = x.log(2f64);
+   |             ^^^^^^^^^^^ help: consider using: `x.log2()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:18:13
+   |
+LL |     let _ = x.log(10f64);
+   |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:19:13
+   |
+LL |     let _ = x.log(std::f64::consts::E);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:24:13
+   |
+LL |     let _ = (1f32 + 2.).ln();
+   |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
+   |
+   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:25:13
+   |
+LL |     let _ = (1f32 + 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:26:13
+   |
+LL |     let _ = (1.0 + x).ln();
+   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:27:13
+   |
+LL |     let _ = (1.0 + x / 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:28:13
+   |
+LL |     let _ = (1.0 + x.powi(2)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:29:13
+   |
+LL |     let _ = (1.0 + x.powi(2) / 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:30:13
+   |
+LL |     let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:31:13
+   |
+LL |     let _ = (x + 1.0).ln();
+   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:32:13
+   |
+LL |     let _ = (x.powi(2) + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:33:13
+   |
+LL |     let _ = (x + 2.0 + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:34:13
+   |
+LL |     let _ = (x / 2.0 + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:42:13
+   |
+LL |     let _ = (1f64 + 2.).ln();
+   |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:43:13
+   |
+LL |     let _ = (1f64 + 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:44:13
+   |
+LL |     let _ = (1.0 + x).ln();
+   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:45:13
+   |
+LL |     let _ = (1.0 + x / 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:46:13
+   |
+LL |     let _ = (1.0 + x.powi(2)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:47:13
+   |
+LL |     let _ = (x + 1.0).ln();
+   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:48:13
+   |
+LL |     let _ = (x.powi(2) + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:49:13
+   |
+LL |     let _ = (x + 2.0 + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+  --> $DIR/floating_point_log.rs:50:13
+   |
+LL |     let _ = (x / 2.0 + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: aborting due to 28 previous errors
+
diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed
new file mode 100644
index 00000000000..e343c37740d
--- /dev/null
+++ b/tests/ui/floating_point_mul_add.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let a: f64 = 1234.567;
+    let b: f64 = 45.67834;
+    let c: f64 = 0.0004;
+    let d: f64 = 0.0001;
+
+    let _ = a.mul_add(b, c);
+    let _ = a.mul_add(b, c);
+    let _ = 2.0f64.mul_add(4.0, a);
+    let _ = 2.0f64.mul_add(4., a);
+
+    let _ = a.mul_add(b, c);
+    let _ = a.mul_add(b, c);
+    let _ = (a * b).mul_add(c, d);
+
+    let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
+    let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
+}
diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs
new file mode 100644
index 00000000000..810f929c856
--- /dev/null
+++ b/tests/ui/floating_point_mul_add.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let a: f64 = 1234.567;
+    let b: f64 = 45.67834;
+    let c: f64 = 0.0004;
+    let d: f64 = 0.0001;
+
+    let _ = a * b + c;
+    let _ = c + a * b;
+    let _ = a + 2.0 * 4.0;
+    let _ = a + 2. * 4.;
+
+    let _ = (a * b) + c;
+    let _ = c + (a * b);
+    let _ = a * b * c + d;
+
+    let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
+    let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+}
diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr
new file mode 100644
index 00000000000..2dfbf562d15
--- /dev/null
+++ b/tests/ui/floating_point_mul_add.stderr
@@ -0,0 +1,58 @@
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:10:13
+   |
+LL |     let _ = a * b + c;
+   |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:11:13
+   |
+LL |     let _ = c + a * b;
+   |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:12:13
+   |
+LL |     let _ = a + 2.0 * 4.0;
+   |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:13:13
+   |
+LL |     let _ = a + 2. * 4.;
+   |             ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:15:13
+   |
+LL |     let _ = (a * b) + c;
+   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:16:13
+   |
+LL |     let _ = c + (a * b);
+   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:17:13
+   |
+LL |     let _ = a * b * c + d;
+   |             ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:19:13
+   |
+LL |     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:20:13
+   |
+LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed
new file mode 100644
index 00000000000..78a9d44829b
--- /dev/null
+++ b/tests/ui/floating_point_powf.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let _ = x.exp2();
+    let _ = 3.1f32.exp2();
+    let _ = (-3.1f32).exp2();
+    let _ = x.exp();
+    let _ = 3.1f32.exp();
+    let _ = (-3.1f32).exp();
+    let _ = x.sqrt();
+    let _ = x.cbrt();
+    let _ = x.powi(2);
+    let _ = x.powi(-2);
+    let _ = x.powi(16_777_215);
+    let _ = x.powi(-16_777_215);
+    // Cases where the lint shouldn't be applied
+    let _ = x.powf(2.1);
+    let _ = x.powf(-2.1);
+    let _ = x.powf(16_777_216.0);
+    let _ = x.powf(-16_777_216.0);
+
+    let x = 3f64;
+    let _ = x.exp2();
+    let _ = 3.1f64.exp2();
+    let _ = (-3.1f64).exp2();
+    let _ = x.exp();
+    let _ = 3.1f64.exp();
+    let _ = (-3.1f64).exp();
+    let _ = x.sqrt();
+    let _ = x.cbrt();
+    let _ = x.powi(2);
+    let _ = x.powi(-2);
+    let _ = x.powi(-2_147_483_648);
+    let _ = x.powi(2_147_483_647);
+    // Cases where the lint shouldn't be applied
+    let _ = x.powf(2.1);
+    let _ = x.powf(-2.1);
+    let _ = x.powf(-2_147_483_649.0);
+    let _ = x.powf(2_147_483_648.0);
+}
diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs
new file mode 100644
index 00000000000..dbc1cac5cb4
--- /dev/null
+++ b/tests/ui/floating_point_powf.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let _ = 2f32.powf(x);
+    let _ = 2f32.powf(3.1);
+    let _ = 2f32.powf(-3.1);
+    let _ = std::f32::consts::E.powf(x);
+    let _ = std::f32::consts::E.powf(3.1);
+    let _ = std::f32::consts::E.powf(-3.1);
+    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(16_777_215.0);
+    let _ = x.powf(-16_777_215.0);
+    // Cases where the lint shouldn't be applied
+    let _ = x.powf(2.1);
+    let _ = x.powf(-2.1);
+    let _ = x.powf(16_777_216.0);
+    let _ = x.powf(-16_777_216.0);
+
+    let x = 3f64;
+    let _ = 2f64.powf(x);
+    let _ = 2f64.powf(3.1);
+    let _ = 2f64.powf(-3.1);
+    let _ = std::f64::consts::E.powf(x);
+    let _ = std::f64::consts::E.powf(3.1);
+    let _ = std::f64::consts::E.powf(-3.1);
+    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_147_483_648.0);
+    let _ = x.powf(2_147_483_647.0);
+    // Cases where the lint shouldn't be applied
+    let _ = x.powf(2.1);
+    let _ = x.powf(-2.1);
+    let _ = x.powf(-2_147_483_649.0);
+    let _ = x.powf(2_147_483_648.0);
+}
diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr
new file mode 100644
index 00000000000..ad5163f0079
--- /dev/null
+++ b/tests/ui/floating_point_powf.stderr
@@ -0,0 +1,150 @@
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:6:13
+   |
+LL |     let _ = 2f32.powf(x);
+   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:7:13
+   |
+LL |     let _ = 2f32.powf(3.1);
+   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:8:13
+   |
+LL |     let _ = 2f32.powf(-3.1);
+   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:9:13
+   |
+LL |     let _ = std::f32::consts::E.powf(x);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:10:13
+   |
+LL |     let _ = std::f32::consts::E.powf(3.1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:11:13
+   |
+LL |     let _ = std::f32::consts::E.powf(-3.1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()`
+
+error: square-root of a number can be computed more efficiently and accurately
+  --> $DIR/floating_point_powf.rs:12: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_powf.rs:13:13
+   |
+LL |     let _ = x.powf(1.0 / 3.0);
+   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
+   |
+   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_powf.rs:14: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_powf.rs:15: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_powf.rs:16:13
+   |
+LL |     let _ = x.powf(16_777_215.0);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_powf.rs:17:13
+   |
+LL |     let _ = x.powf(-16_777_215.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:25: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_powf.rs:26:13
+   |
+LL |     let _ = 2f64.powf(3.1);
+   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:27:13
+   |
+LL |     let _ = 2f64.powf(-3.1);
+   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:28:13
+   |
+LL |     let _ = std::f64::consts::E.powf(x);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:29:13
+   |
+LL |     let _ = std::f64::consts::E.powf(3.1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+  --> $DIR/floating_point_powf.rs:30:13
+   |
+LL |     let _ = std::f64::consts::E.powf(-3.1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
+
+error: square-root of a number can be computed more efficiently and accurately
+  --> $DIR/floating_point_powf.rs:31: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_powf.rs:32: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_powf.rs:33: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_powf.rs:34: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_powf.rs:35:13
+   |
+LL |     let _ = x.powf(-2_147_483_648.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_powf.rs:36:13
+   |
+LL |     let _ = x.powf(2_147_483_647.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
+
+error: aborting due to 24 previous errors
+
diff --git a/tests/ui/mul_add.rs b/tests/ui/mul_add.rs
deleted file mode 100644
index 1322e002c64..00000000000
--- a/tests/ui/mul_add.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![warn(clippy::manual_mul_add)]
-#![allow(unused_variables)]
-
-fn mul_add_test() {
-    let a: f64 = 1234.567;
-    let b: f64 = 45.67834;
-    let c: f64 = 0.0004;
-
-    // Examples of not auto-fixable expressions
-    let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c);
-    let test2 = 1234.567 * 45.67834 + 0.0004;
-}
-
-fn main() {
-    mul_add_test();
-}
diff --git a/tests/ui/mul_add.stderr b/tests/ui/mul_add.stderr
deleted file mode 100644
index 3b21646f7c3..00000000000
--- a/tests/ui/mul_add.stderr
+++ /dev/null
@@ -1,34 +0,0 @@
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add.rs:10:17
-   |
-LL |     let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(a * b + c).mul_add((c + a * b), (c + (a * b) + c))`
-   |
-   = note: `-D clippy::manual-mul-add` implied by `-D warnings`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add.rs:10:17
-   |
-LL |     let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c);
-   |                 ^^^^^^^^^^^ help: try: `a.mul_add(b, c)`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add.rs:10:31
-   |
-LL |     let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c);
-   |                               ^^^^^^^^^^^ help: try: `a.mul_add(b, c)`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add.rs:10:46
-   |
-LL |     let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c);
-   |                                              ^^^^^^^^^^^ help: try: `a.mul_add(b, c)`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add.rs:11:17
-   |
-LL |     let test2 = 1234.567 * 45.67834 + 0.0004;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1234.567.mul_add(45.67834, 0.0004)`
-
-error: aborting due to 5 previous errors
-
diff --git a/tests/ui/mul_add_fixable.fixed b/tests/ui/mul_add_fixable.fixed
deleted file mode 100644
index 4af7c7e3e1a..00000000000
--- a/tests/ui/mul_add_fixable.fixed
+++ /dev/null
@@ -1,24 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::manual_mul_add)]
-#![allow(unused_variables)]
-
-fn mul_add_test() {
-    let a: f64 = 1234.567;
-    let b: f64 = 45.67834;
-    let c: f64 = 0.0004;
-
-    // Auto-fixable examples
-    let test1 = a.mul_add(b, c);
-    let test2 = a.mul_add(b, c);
-
-    let test3 = a.mul_add(b, c);
-    let test4 = a.mul_add(b, c);
-
-    let test5 = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
-    let test6 = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
-}
-
-fn main() {
-    mul_add_test();
-}
diff --git a/tests/ui/mul_add_fixable.rs b/tests/ui/mul_add_fixable.rs
deleted file mode 100644
index 8b42f6f184a..00000000000
--- a/tests/ui/mul_add_fixable.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::manual_mul_add)]
-#![allow(unused_variables)]
-
-fn mul_add_test() {
-    let a: f64 = 1234.567;
-    let b: f64 = 45.67834;
-    let c: f64 = 0.0004;
-
-    // Auto-fixable examples
-    let test1 = a * b + c;
-    let test2 = c + a * b;
-
-    let test3 = (a * b) + c;
-    let test4 = c + (a * b);
-
-    let test5 = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
-    let test6 = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
-}
-
-fn main() {
-    mul_add_test();
-}
diff --git a/tests/ui/mul_add_fixable.stderr b/tests/ui/mul_add_fixable.stderr
deleted file mode 100644
index 235443f4b02..00000000000
--- a/tests/ui/mul_add_fixable.stderr
+++ /dev/null
@@ -1,40 +0,0 @@
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add_fixable.rs:12:17
-   |
-LL |     let test1 = a * b + c;
-   |                 ^^^^^^^^^ help: try: `a.mul_add(b, c)`
-   |
-   = note: `-D clippy::manual-mul-add` implied by `-D warnings`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add_fixable.rs:13:17
-   |
-LL |     let test2 = c + a * b;
-   |                 ^^^^^^^^^ help: try: `a.mul_add(b, c)`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add_fixable.rs:15:17
-   |
-LL |     let test3 = (a * b) + c;
-   |                 ^^^^^^^^^^^ help: try: `a.mul_add(b, c)`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add_fixable.rs:16:17
-   |
-LL |     let test4 = c + (a * b);
-   |                 ^^^^^^^^^^^ help: try: `a.mul_add(b, c)`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add_fixable.rs:18:17
-   |
-LL |     let test5 = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
-
-error: consider using `mul_add()` for better numerical precision
-  --> $DIR/mul_add_fixable.rs:19:17
-   |
-LL |     let test6 = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
-
-error: aborting due to 6 previous errors
-