【问题标题】:Why write 1,000,000,000 as 1000*1000*1000 in C?为什么在 C 中将 1,000,000,000 写为 1000*1000*1000?
【发布时间】:2017-03-30 16:56:49
【问题描述】:

在 Apple 创建的代码中,有这样一行:

CMTimeMakeWithSeconds( newDurationSeconds, 1000*1000*1000 )

有什么理由将1,000,000,000 表示为1000*1000*1000

为什么不用1000^3 呢?

【问题讨论】:

  • 代码的清晰度和可读性。您不能将 , 或 ' 作为 C 中的分隔符,因此下一个最好的方法是从乘法中计算值。 ^ 是 C 中不相关的运算符 - 异或。
  • 它特别用于持续时间:2*60*60 .. 它很容易注意到是 2 小时..
  • 我怀疑 1,000,000,000 不是有效的语法
  • 顺便说一句,现代编程语言有替代方法来编写相同的,例如在斯威夫特1_000_000_000。然而,对于时间常数,这更加困难。写30 * 60(30 分钟以秒为单位)比写1800 更易读。实际上有一些语言可以让你编写单元,例如meters,让您保护自己免受不良作业的影响。
  • ^ 是 XOR,而不是指数或幂运算符。

标签: objective-c c integer literals


【解决方案1】:

有理由使用1000 * 1000 * 1000

使用 16 位 int1000 * 1000 溢出。所以使用 1000 * 1000 * 1000 会降低可移植性。

使用 32 位 int,以下第一行代码溢出。

long long Duration = 1000 * 1000 * 1000 * 1000;  // overflow
long long Duration = 1000000000000;  // no overflow, hard to read

为了可读性、可移植性和正确性,建议前导值与目标类型相匹配。

double Duration = 1000.0 * 1000 * 1000;
long long Duration = 1000LL * 1000 * 1000 * 1000;

代码也可以简单地使用e 表示法来表示完全可以表示为double 的值。当然,这会导致知道double 是否可以精确地表示整数值——对于大于 1e9 的值会引起关注。 (见DBL_EPSILONDBL_DIG)。

long Duration = 1000000000;
// vs.
long Duration = 1e9;

【讨论】:

  • 非常重要的评论! securecoding.cert.org/confluence/display/c/… 在很多情况下可能会有所帮助?
  • A double 可以精确表示最大为 2^53 ≈ 9e15 的所有整数。
  • @EdgarBonet 确实,binary64 可以代表大约 9e15 的整数。但是 C 没有指定 double 使用 binary64,尽管它非常常用。根据 C 规范,高达 1e9 左右的值是完全可表示的。这取决于您是要编写规范还是依赖常规做法。
  • @Patrick 10001000000000000 都是整数常量。每个独立具有从intlonglong long 中选择的类型。编译器使用 integer constant 适合的那 3 种类型中的第一种。 1000 * 1000 * 1000 * 1000 使用 int 数学作为 int 中的每个 1000 完成。产品溢出 32 位 int1000000000000 当然可以表示为long long(或者可能更窄)——没有溢出。目标long long Duration 的类型不会影响这个“=”确定的右侧。
  • 在乘法中首先放置较宽的类型很重要。对于 16 位 intlong x = 1000 * 1000 * 1000L; 会溢出,而 long x = 1000L * 1000 * 1000; 不会。
【解决方案2】:

在 C 中为十进制数实现类似效果的另一种方法是使用文字浮点表示法——只要双精度可以表示您想要的数字而不会损失任何精度。

IEEE 754 64 位双精度可以表示任何非负整数

long lots_of_secs = 1e9;

【讨论】:

    【解决方案3】:

    以乘法方式声明常量的一个原因是为了提高可读性,同时不影响运行时性能。 此外,表明作者正在以乘法的方式思考数字。

    考虑一下:

    double memoryBytes = 1024 * 1024 * 1024;
    

    明显优于:

    double memoryBytes = 1073741824;
    

    因为后者乍一看不像 1024 的三次方。

    正如 Amin Negm-Awad 所提到的,^ 运算符是二进制 XOR。许多语言缺乏内置的编译时取幂运算符,因此没有乘法。

    【讨论】:

    • 在确实有指数运算符的语言中,它不一定是'^'。例如,在 Fortran 中,它是 '**'。
    • 您还应该包含一个指向重要警告的链接,在下面的答案中给出,来自@chux:stackoverflow.com/a/40637622/1841533(特别是当 OP 标记为“c”时,它很容易受到这种“权利”的影响- 手边操作似乎将所有项限制为较小的类型,因此乘法可能会溢出问题)。 securecoding.cert.org/confluence/display/c/… 可能有助于避免一般情况下的那些?
    • 另外我们应该注意计算是在编译时完成的。 C 标准要求实现能够在编译时为该语言的各种特性计算常量表达式,我们可以安全地假设当像本例中那样使用常量表达式时它是正确的。
    • 双倍存储内存?这似乎是一个潜在的错误来源。
    • @supercat 我知道这一点,但是通过使用 double 你可能会遇到这样的情况,例如,你想要一部分内存范围,你除以 x 以获得大小子范围......突然你有一个小数字节,这可能需要额外的逻辑来补偿。
    【解决方案4】:

    可能更容易阅读并与1,000,000,000 表单建立一些关联。

    从技术方面来说,我猜直接数或乘法之间没有区别。无论如何,编译器都会将其生成为恒定的十亿数字。

    如果您谈论objective-c,那么1000^3 将不起作用,因为pow 没有这样的语法(它是xor)。相反,可以使用pow() 函数。但在这种情况下,它不会是最优的,它将是一个运行时函数调用,而不是编译器生成的常量。

    【讨论】:

      【解决方案5】:

      为了可读性。作为比较,Java 在数量上支持 _ 以提高可读性(由 Stephen Colebourne 首次提出作为 Project Coin/JSR 334 的 reply to Derek Foster's PROPOSAL: Binary Literals)。可以在这里写1_000_000_000

      大致按时间顺序,从最旧的支持到最新:

      这是一个相对较新的特性,让语言意识到它们应该支持(然后是 Perl)。正如 chux@ 的出色回答一样,1000*1000... 是一个部分解决方案,但即使最终结果是一个大类型,程序员也会面临乘法溢出的错误。

      【讨论】:

      • 许多现代编程语言都有相同的,例如迅速。没什么新鲜的。
      • AFAIK,这来自 Perl。 PL/M 使用 $ 用于相同目的,例如:0100$0010B
      • 不过,它相当新的。 Java 特性可能已有 5 年的历史。大多数支持这种语法的其他语言都是相当新的——Swift 本身只有几年的历史。 Python 在尚未发布的 3.6 中添加了支持。
      • Ada 支持整数文字中的下划线已有 33 年了。
      • @djechlin:我冒昧地添加了更多信息,大致按时间顺序排列。我之前弄错了,从 Project Coin 线程来看,Stephen Colebourne 可能从 Fandom 和/或 Ruby 中接受了整数文字中的下划线的想法。 Ruby 可能从 Perl 那里得到了这个想法,而 Perl 从 Ada 那里得到了这个想法。
      【解决方案6】:

      为了说明原因考虑以下测试程序:

      $ cat comma-expr.c && gcc -o comma-expr comma-expr.c && ./comma-expr
      #include <stdio.h>
      
      #define BILLION1 (1,000,000,000)
      #define BILLION2 (1000^3)
      
      int main()
      {
              printf("%d, %d\n", BILLION1, BILLION2);
      }
      0, 1003
      $
      

      【讨论】:

      • @pjvandehaar 我不建议通过阅读维基百科文章来学习一门语言。
      【解决方案7】:

      为什么不1000^3

      1000^3 的结果是 1003。^ 是位异或运算符。

      即使它不涉及 Q 本身,我也添加一个说明。 x^y 确实 not 总是评估为 x+y 就像在提问者的示例中那样。你必须对每一位进行异或。在例子的情况下:

      1111101000₂ (1000₁₀)
      0000000011₂ (3₁₀)
      1111101011₂ (1003₁₀)
      

      但是

      1111101001₂ (1001₁₀)
      0000000011₂ (3₁₀)
      1111101010₂ (1002₁₀)
      

      【讨论】:

      • 先生,我不清楚 1003^3 是 1003。Google 和 Mac 计算器显示 1000^3 = 1,000,000,000。你能解释一下吗?
      • ^ 运算符在 C/C++/Objective-C 等中表示 XOR。在计算器中,它通常表示 x-to-the-y 幂。
      • 呸,1000 和 3 的位不重叠。这看起来错了。
      • 位确实重叠。但不是1。 :-]
      • @Yakk:确实,它看起来很不对劲! ...我希望很多人不会认为“A^B”总是给出 A+B(但我担心有些人可能...)
      【解决方案8】:

      为了可读性。

      在零之间放置逗号和空格(1 000 000 0001,000,000,000)会产生语法错误,并且在代码中包含 1000000000 会让人很难确切地看到有多少个零。

      1000*1000*1000 很明显它是 10^9,因为我们的眼睛可以更容易地处理这些块。此外,没有运行时成本,因为编译器会将其替换为常量1000000000

      【讨论】:

      • 仅供参考,我最近了解了一个数字分隔符的概念。 Java 已经有一段时间了,C# 7.0 可能会得到它。我希望所有的语言都有这个吸引眼球的功能。 :)
      • 根据上下文使用1,000,000,000 不会产生语法错误,它只是意味着别的东西。例如CMTimeMakeWithSeconds( newDurationSeconds, 1,000,000,000 )
      • @JMS10 C#如果安装VS15预览版已经有,可以写成1_000_000_000
      • Python 也将_ 作为分隔符:)
      • 而 C++ 最近在 C++14 中获得了' 分隔符,因此您可以使用1'000'000'000。 (之所以选择它是因为 1,000,000,000 可能被误解为逗号运算符或 4 个不同的参数,而 _1_000_000_000 是一个有效的(但可能是错误的)变量名。)
      猜你喜欢
      • 1970-01-01
      • 2017-08-09
      • 2014-11-06
      • 2011-10-05
      • 2018-07-19
      • 1970-01-01
      • 2021-09-18
      • 2012-07-07
      相关资源
      最近更新 更多