使用 cmath 的 fmod (C++) 时的不一致

Posted

技术标签:

【中文标题】使用 cmath 的 fmod (C++) 时的不一致【英文标题】:Inconsistencies when using cmath's fmod (C++) 【发布时间】:2014-03-28 03:03:24 【问题描述】:

使用 fmod 函数时,我得到了一些非常令人困惑的结果。

以下代码:

double x = pow(142, 35);
double y = fmod(x, 221);
std::cout << x << std::endl << y;

输出:

2.13842e+75
206

但是当对 x 值进行硬编码时:

double x = pow(142, 35);
double y = fmod(2.13842e+75, 221);
std::cout << x << std::endl << y;

输出改为:

2.13842e+75
14

我不知道这是什么原因,它在我的程序中产生了一些丑陋的错误。任何见解将不胜感激。提前致谢。

【问题讨论】:

使用模块化幂算法更新答案,应该可以解决您的问题。 【参考方案1】:

所以当我像这样输出第一个结果时:

std::cout << std::fixed << x << std::endl << y << std::endl;

我看到了:

2138415301692701661114266637060519453227273059369895888628790658837784821760.000000
206.000000

当我将上面的这个数字用于xs 值时,如下所示:

double y = fmod(2138415301692701661114266637060519453227273059369895888628790658837784821760.000000, 221);

然后我从第一个示例中得到206 for y 的结果,主要问题是您使用IEEE double 达到了限制。

更新

此算法适用于modular power:

template <typename T>
T modpow(T base, T exp, T modulus) 
  base %= modulus;
  T result = 1;
  while (exp > 0) 
    if (exp & 1) result = (result * base) % modulus;
    base = (base * base) % modulus;
    exp >>= 1;
  
  return result;

我从here 找到的内容将为您提供正确的结果。

【讨论】:

那时我很困惑。如果您在 wolfram alpha 中键入 142^35 mod 221,则结果为 12。我猜 14 不是我之前提到的所需结果。该代码用于 RSA 解密,因此结果应该在 0 到 25 之间,并且对应于一个字母字符。 12 是正确答案。即使是双精度数也不足以容纳答案。 142^35的实际值为2138415301692701650291828893190357329823022421032574372998179511073104723968;只有前 17 位数字匹配。您需要一个任意精度的计算器,例如 Linux 上的 bc 才能正确执行计算。 @esorton 我只是在检查,这是我的怀疑 @legends2k 似乎和它通常用于 RSA,这是 OP 使用它的目的,请参阅 Wikipedia article on it。 是的,我不知道这个,谢谢!而不是首先计算功率(这将导致本机数据类型溢出)然后尝试修改它,这两者都在限制范围内,很高兴知道:)【参考方案2】:

这里有几个问题。

首先,您的打印语句会截断实际值。 C++ 的默认值 cout 的精度为 6。它只会打印 6 位小数。就这样 以下两次打印会产生不同的结果,即使它们打印相同 变量:

double x = pow(142, 35);
std::cout << x << std::endl << y;
// prints 2.13842e+75
std::cout << std::fixed << x << std::endl << y << std::endl;
// prints 2138415301692701661114266637060519453227273059369895888628790658837784821760.0000000

请注意,std::fixed 操纵器用于更改输出。谢谢 @Shafik Yaghmour 提供了机械手。

第二,即使上面打印的值也不正确,因为精度不够 双的。 IEEE double 使用 52 位来存储尾数。这是 足以达到 15 到 17 位的精度。您需要使用仲裁 精度计算器来确定实际结果(例如,bc 命令行实用程序)。

% bc
142^35
2138415301692701650291828893190357329823022421032574372998179511073104723968
(142^35)%221
12

查看这两个值,前 17 位数字符合预期。

见http://en.wikipedia.org/wiki/Double-precision_floating-point_format 有关 IEEE double 限制的更多详细信息。

【讨论】:

以上是关于使用 cmath 的 fmod (C++) 时的不一致的主要内容,如果未能解决你的问题,请参考以下文章

double long float类型读入读出 double取模 fmod

在 Fmod Studio C++ 中保存 FFT 谱

编组 LPSTR 和浮动时的不平衡堆栈

c++11 下 fmod(和其他)的不同行为,至少在 Visual Studio 中

如何在 FMOD Ex 中实现 VST 插件?

% 在 rand() c++ 之后不起作用