避免溢出工作模型p

Avoiding overflow working modulo p

本文关键字:模型 工作 溢出      更新时间:2023-10-16

作为大学作业的一部分,我必须在椭圆曲线p = 2^255-19上在C标量乘法中实现。使用原始类型(未签名长(。

但是,如果a和b是两个整数模块,则有溢出计算A*b的风险。我不确定如何避免这种情况。以下代码正确吗?

long a = ...;
long b = ...;
long c = (a * b) % p;

或我宁愿先施放A和B?

long a = ...;
long b = ...;
long long a1 = (long long) a;
long long b1 = (long long) b;
long c = (long) ((a1 * b1) % p);

我也一直在思考或与漫长的长时间合作。

整个操作(乘法(正在牢记操作数的类型。您乘以两个long变量,结果如果大于long变量所能保留的结果,则会溢出。

((a%p)*(b%p))%p这给出了一个保护它围绕p包裹的保护,但是在较早的情况下仍说出的话仍然可以-(a%p)*(b%p)仍然可以溢出。(考虑到ab是类型long(。

如果将long的值存储在long long中,则无需施放。但是,是的,当乘法得出的值大于long long时,结果将溢出。

给您一个澄清: -

long a,b;
..
long long p = (a*b)%m;

这无济于事。完成后的乘法为long算术。我们存储最终结果的位置都没关系。这取决于操作数的类型。

现在看这个

long c = (long) ((a1 * b1) % p);这里的结果将是两个long long乘法,并且将基于最大值long long溢出,但是当您将其分配给long时,仍然有可能溢出。

如果p255字节,您将无法意识到使用内置类型long或使用3264 BIT系统的内置类型的内置类型。当我们拥有512位系统时,这肯定是可能的。还需要注意的一件事是何时p=2 255 -19,那么与之进行模块化算术几乎没有任何实用性。

如果sizeof long如ILP64和LP64中的sizeof long long等于CC_27,则使用long,并且long long不会给您带来任何结果。但是,如果sizeof long long大于sizeof long,则可以在long long中保存操作数以防止乘法的溢出。

另一种解决方法是编写自己的大整数库(多个精确整数库(或使用已经存在的大整数库(也许是这样(。这个想法围绕以下事实:较大的类型是使用像char这样的简单的东西实现的,然后在其上进行操作。这是一个实现问题,围绕同一主题有许多实现。

具有255 位整数要求,标准操作和C库不足。

在一般算法中遵循以编写自己的模块化乘法。

myint mod(myint a, myint m);
myint add(myint a, myint b);  // this may overflow
int   cmp(myint a, myint b);
int   isodd(myint a);
myint halve(myint a);
// (a+b)%mod
myint addmodmax(myint a, myint b, myint m) {
  myint sum = add(a,b);
  if (cmp(sum,a) < 0) {
    sum = add(mod(add(sum, 1),m), mod(myint_MAX,m)); // These additions do not overflow
  }
  return mod(sum, m);
}
// (a*b)%mod
myint mulmodmax(myint a, myint b, myint m) {
  myint prod = 0;
  while (cmp(b,0) > 0) {
    if (isodd(b)) {
      prod = addmodmax(prod, a, m);
    }
    b = halve(b);
    a = addmodmax(a, a, m);
  }
  return prod;
}

我最近遇到了同样的问题。

首先,我将假设您的意思是32- bit 整数(阅读您的评论后(,但我认为这也适用于大整数(因为进行天真的乘法意味着要加倍单词大小,也将很慢(。

选项1

我们使用以下属性:

命题。 a*b mod m = (a - m)*(b - m) mod m
证明。

(a - m)*(b - m) mod m =  
(a*b - (a+b)*m + m^2) mod m =  
(a*b mod m - ((a+b) + m)*m mod m) mod m =  
(a*b mod m) mod m = a*b mod m

q.e.d。

此外,如果a,b大约m,则(a - m)*(b - m) mod m = (a - m)*(b - m)。您需要解决a,b> m的何时,但是我认为(m - a)*(m - b) mod m = a*b mod m的有效性是上述命题的必然性。当然,当差异很大时,不要这样做(小模量,大a或b;反之亦然(,或者会溢出。

选项2

来自Wikipedia

uint64_t mul_mod(uint64_t a, uint64_t b, uint64_t m)
{
   uint64_t d = 0, mp2 = m >> 1;
   int i;
   if (a >= m) a %= m;
   if (b >= m) b %= m;
   for (i = 0; i < 64; ++i)
   {
       d = (d > mp2) ? (d << 1) - m : d << 1;
       if (a & 0x8000000000000000ULL)
           d += b;
       if (d >= m) d -= m;
       a <<= 1;
   }
   return d;
}

以及假设long double和32或64位整数(不是任意精度(您可以在不同类型的大多数重要位置上利用机器优先级:

在计算机体系结构上,可以使用至少64位Mantissa(例如大多数X86 C编译器的长双重类型(的扩展精度格式,以下例程要比任何算法解决方案都快,通过采用一个技巧,该技巧是通过硬件,浮点乘法会导致产品的最重要位,而整数乘法会导致保留最低的位置

和:

uint64_t mul_mod(uint64_t a, uint64_t b, uint64_t m)
{
   long double x;
   uint64_t c;
   int64_t r;
   if (a >= m) a %= m;
   if (b >= m) b %= m;
   x = a;
   c = x * b / m;
   r = (int64_t)(a * b - c * m) % (int64_t)m;
   return r < 0 ? r + m : r;
}

保证这些不会溢出。