【问题标题】:Why the digits after decimal are all zero?为什么小数点后的数字都是零?
【发布时间】:2013-04-02 15:03:37
【问题描述】:

我想执行一些计算,我希望结果正确到小数位,比如 12。 于是我写了一个样例:

#define PI 3.1415926535897932384626433832795028841971693993751
double d, k, h;
k = 999999/(2*PI);
h = 999999;
d = PI*k*k*h;
printf("%.12f\n", d);

但它给出了输出:

79577232813771760.000000000000

我什至使用了 setprecision(),但答案相同,而不是指数形式。

cout<<setprecision(12)<<d<<endl;

打印

7.95772328138e+16

也用过long double,但是没有用。

除了将整数部分和小数部分分别存储在 long long int 类型中之外,还有其他方法吗?

如果是这样,如何才能准确地得到答案?

【问题讨论】:

  • @EricLeschinski:整数乘以双精度数得到双精度数。
  • 您的号码中有 16 位小数;你不能对double 期待更多。 (有时,您可以得到 17;之后,您会得到随机值 - 或零。)阅读“What Every Computer Scientist Should Know About Floating Point Arithmetic”论文 - 很容易在 SO 或 Internet 上找到。
  • 一个双精度可以存储大约 16 位十进制数字,这似乎是您得到的大致内容?您希望得到什么结果?
  • @Sunny:不,不需要。在某些系统上,long doubledouble 相同。

标签: c++ c floating-point-precision


【解决方案1】:

双精度只有大约 16 位十进制数字。小数点后的一切都是无稽之谈。 (事实上​​,该点的最后一位或两位数可能与无限精度计算不一致。)

长双未标准化,AFAIK。可能在您的系统上它与 double 相同,或者不再精确。这会让我有点吃惊,但它并没有违反任何规定。

【讨论】:

  • 那么先生,我还有什么其他选择可以得到准确的结果?
  • 只是为了成为我大学物理时代的纯粹主义者,我要补充一点,“999999”值后面没有小数声明,这意味着他不应该寻找超过他的回答精确到六位数。但这是分裂的头发。事实仍然存在,他要求的精度比替身所能提供的要高。
  • 为超精度算术设置了几个库。另一个答案提到了 Gnu 多精度 (GMP) 库[http://gmplib.org/]。您应该意识到很少有真实世界的工程应用程序对小数点后 17 位有任何意义。如果不是任意练习,您可以与我们分享您正在尝试做的事情。
  • @AndrewLazarus 我不想为此使用外部库。我希望我的代码是标准的。这是比赛中问题的一部分,无论如何我都不能使用标准以外的库。
  • @Sunny:那么你必须编写自己的支持更高精度的数字类,因为标准不支持它。
【解决方案2】:

你需要再次阅读Double-Precision概念; 更加小心

double 使用 64 位提高了精度。
小数点前的东西比小数点后的更重要。
所以,当你有一个大整数部分时,它会截断较低的精度——这在各种答案中被描述为rounding off


更新
为了提高精度,您需要使用一些库或更改您的语言。
检查另一个问题:Best coding language for dealing with large numbers (50000+ digits)

但是,我会要求您再次重新检查您的意图。

  • 对于具有非常高值的数字,您真的需要 12 位小数
    (如您的示例中的整数部分超过 10 位)?
  • 也许你真的没有大整数部分
    (在这种情况下,这样的代码应该可以正常工作)。
  • 但如果您正在跟踪像 10000000000.123456789 这样的值,
    我对您正在开发的应用程序(天文学?)非常感兴趣。
  • 如果您的值的整数部分低于10000,您应该没问题。

更新2
如果您必须证明特定公式能够在受约束的错误限制内准确工作,那么解决方法是修复您的公式处理,以便引入最少的错误。

例子,

  • 如果你想说,(x * y) / z
  • 谨慎的做法是尝试类似max(x,y)/z * min(x,y)
  • 而不是在(x * y) 之后可能溢出的原始形式,如果不适合double 的16 位小数,则会失去精度

如果你只有 2 位精度,

.               2-digit       regular-precision
 `42 * 7        290           297
 (42 * 7)/2     290/2         294/2
 Result ==>     145           147

       But ==>  42/2 = 21
                21 * 7 = 147

这可能是你比赛的目的。

【讨论】:

  • 这解释了全零的行为,但我仍然对问题的另一部分一无所知,如何仍然得到结果?
  • 正如我在某处评论的那样,这是比赛问题的一部分,当且仅当 |A - B| 时,我的答案 A(正确输出 B)将被视为正确。 ≤ 10^-11 * B
  • 这是一个很好的提示。让我再试试,然后我会通知你。非常感谢。
【解决方案3】:

大多数计算机使用的双精度二进制格式只能容纳大约 16 位数字,之后您将进行四舍五入。见http://en.wikipedia.org/wiki/Double-precision_floating-point_format

【讨论】:

    【解决方案4】:

    浮点值具有数字的限制范围。仅仅因为您的“PI”值的位数是double 的六倍,将不会改变硬件的工作方式。

    典型的 (IEEE754) double 将产生大约 15-16 位小数。无论是 0.12345678901235、1234567.8901235、12345678901235 还是 12345678901235000000000 或其他一些变化。

    换句话说,是的,如果您精确计算,您会得到很多小数位,因为pi 永远不会结束。在计算机上,无论您使用什么输入值,您都会得到大约 15 到 16 位数字——所有变化都是小数点在该序列中的位置。要获得更多,您需要“大量支持”,例如 Gnu Multiprcession (GMP) 库。

    【讨论】:

      【解决方案5】:

      你正在寻找std::fixed这告诉ostream使用指数形式。

      cout << setprecision(12) << std::fixed << d << endl;
      

      【讨论】:

      • 我试过了,这根本行不通。与使用 printf 的输出相同。
      • @tacp 啊,你想要 12 个小数位 过去“个位” - 而不是 12 个小数位 total。然后看其他答案。他们很好。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-07
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多