【问题标题】:Are 0.0 and 1.0 considered magic numbers?0.0 和 1.0 是否被视为幻数?
【发布时间】:2013-10-31 13:01:16
【问题描述】:

我知道-1012 是幻数规则的例外。但是我想知道当它们是浮动时是否也是如此。我是否必须为它们初始化最终变量,或者我可以直接在我的程序中使用它们。

我在课堂上使用它作为百分比。如果输入小于 0.0 或大于 1.0,那么我希望它自动将百分比设置为零。所以如果 (0.0

谢谢

【问题讨论】:

  • 你没有“必须”。这是一种风格。
  • 不过,您的代码的未来维护者会感谢您将您的幻数放在一个常数中,这样他们就不必猜测它们的含义了。
  • 如果一个常量只会被使用一次具有特定含义,并且在使用它的上下文之外没有任何意义,那么在此处添加注释通常比定义一个更好姓名。如果多次使用相同的数值但具有不同的含义,则使用相同的名称来表示多种含义比使用原始数字常量更糟糕。

标签: java coding-style floating-point magic-numbers


【解决方案1】:

这些数字并不是幻数规则的真正例外。常识规则(只要有“一个”规则),当它没有被简化到教条的水平时,基本上是,“不要在不明确其含义的上下文中使用数字。”碰巧这四个数字在明显的上下文中非常常用。这并不意味着它们是唯一适用的数字,例如如果我有:

long kilometersToMeters(int km) { return km * 1000L; }

命名数字确实没有意义:从微小的上下文中可以明显看出它是一个转换因子。另一方面,如果我在一些低级代码中这样做:

sendCommandToDevice(1);

它仍然是错误的,因为它应该是一个常量kResetCommand = 1 或类似的东西。

所以0.01.0 是否应该被一个常量替换完全取决于上下文。

【讨论】:

    【解决方案2】:

    这真的取决于上下文。避免幻数的重点是保持代码的可读性。使用您的最佳判断,或向我们提供一些背景信息,以便我们使用我们的判断。

    幻数是[u]nique values with unexplained meaning or multiple occurrences which could (preferably) be replaced with named constants

    http://en.wikipedia.org/wiki/Magic_number_(programming)

    编辑:何时使用变量名称记录代码与何时仅使用数字是一个备受争议的话题。我的观点是上面链接的 Wiki 文章的作者的观点:如果含义不是很明显并且它在您的代码中多次出现,请使用命名常量。如果只出现一次,只需注释代码即可。

    如果您对其他人的(强烈偏见的)意见感兴趣,请阅读 What is self-documenting code and can it replace well documented code?

    【讨论】:

    • 我在课堂上使用它作为百分比。如果输入小于 0.0 或大于 1.0,那么我希望它自动将百分比设置为零。所以如果 (0.0
    • 如果这个边界在某个地方很明显(例如,一个名为EnforceBounds() 的函数、一个{ set; } 访问器等),那么我只想评论为什么你会绑定到那些数字而不是使用变量名来记录您的代码。
    【解决方案3】:

    通常,每条规则都有例外(这个也有例外)。为这些常量使用一些助记符名称是一种风格问题。

    例如:

    int Rows = 2;
    int Cols = 2;
    

    这是一个非常有效的示例,其中使用原始值会产生误导。

    幻数的含义应该从上下文中显而易见。如果不是 - 给它一个名字。

    【讨论】:

      【解决方案4】:

      为某物附加一个名称会创建一个身份。给定定义

      const double Moe = 2.0;
      const double Joe = 2.0;
      ...
      double Larry = Moe;
      double Harry = Moe;
      double Garry = Joe;
      

      Moe 和 Joe 的符号使用表明 Larry 和 Harry 的默认值彼此相关,而 Garry 的默认值则没有。是否为特定常量定义名称的决定不应取决于该常量的值,而是取决于它是否会非巧合地出现在代码中的多个位置。如果一个人正在与需要向其发送特定字节值以触发重置的远程设备通信,我会考虑:

      void ResetDevice()
      {
        // The 0xF9 command is described in the REMOTE_RESET section of the
        // Frobnitz 9000 manual
        transmitByte(0xF9);
      }
      ... elsewhere
      myDevice.ResetDevice();
      ...
      otherDevice.ResetDevice();
      

      在许多情况下优于

      // The 0xF9 command is described in the REMOTE_RESET section of the
      // Frobnitz 9000 manual
      const int FrobnitzResetCode = 0xF9;
      ... elsewhere
      myDevice.transmitByte(FrobnitzResetCode );
      ...
      otherDevice.transmitByte(FrobnitzResetCode );
      

      值 0xF9 在重置 Frobnitz 9000 设备的上下文之外没有实际意义。除非出于某种原因,外部代码应该更喜欢自己发送必要的值而不是调用 ResetDevice 方法,否则该常量应该对方法之外的任何代码没有任何价值。虽然也许可以使用

      void ResetDevice()
      {
        // The 0xF9 command is described in the REMOTE_RESET section of the
        // Frobnitz 9000 manual
        int FrobnitzResetCode = 0xF9;
        transmitByte(FrobnitzResetCode);
      }
      

      在如此狭隘的背景下为事物定义名称确实没有多大意义。

      关于 0 和 1 等值的唯一“特殊”之处在于,它们的使用频率明显高于其他常量,例如23 在它们在使用它们的上下文之外没有特定于域的身份的情况下。如果使用的函数要求第一个参数指示附加参数的数量(在 C 中有些常见),最好这样说:

      output_multiple_strings(4, "Bob", Joe, Larry, "Fred"); // There are 4 arguments
      ...
      output_multiple_strings(4, "George", Fred, "James", Lucy);  // There are 4 arguments
      

      比 #define NUMBER_OF_STRINGS 4 // 有 4 个参数

      output_multiple_strings(NUMBER_OF_STRINGS, "Bob", Joe, Larry, "Fred");
      ...
      output_multiple_strings(NUMBER_OF_STRINGS, "George", Fred, "James", Lucy);
      

      后一个语句意味着传递给第一个方法的值和传递给第二个方法的值之间的联系比传递给第一个方法的值和该方法调用中的任何其他内容之间存在的联系更强。除其他事项外,如果需要更改其中一个调用以传递 5 个参数,则在第二个代码示例中不清楚应该更改什么以允许这样做。相比之下,在前一个样本中,常数“4”应改为“5”。

      【讨论】:

        猜你喜欢
        • 2019-08-04
        • 2017-12-18
        • 2011-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多