将sqrt(n)与理性p / q进行比较

时间:2018-02-03 11:31:32

标签: performance math floating-point

您将获得一个整数 n 和一个理性 p / q p q 是整数)

如何比较 sqrt(n) p / q

解决方案1 ​​sqrt(n) <= (double) p / q
应该工作,但调用sqrt比使用乘法/除法慢。{/ p>

解决方案2: (double) n * q * q <= p * p
更好,但我无法思考,因为我们使用浮动,如果 p / q 非常接近 sqrt(n),我们可能得到错误的答案。此外,它需要将整数转换为浮点数,这比使用整数更慢(略微)。

解决方案3 n*q*q <= p*p
更好的是,如果由于溢出而导致 p q 变大,则会遇到麻烦(通常,如果 p q &gt; = 2 ^ 32。

解决方案4 :使用带有bignum库的解决方案3 /使用具有未绑定整数的编程语言。

解决方案5: (q / p) * n <= p / q
成功避免任何溢出问题,但我不确定这在所有情况下都是正确的,因为整数除法......

所以...我很乐意选择解决方案2或4,但我想知道是否有人有巧妙的技巧来解决这个问题,或者可能是解决方案5工作(或不工作)的证明(或反例)。

2 个答案:

答案 0 :(得分:3)

正如我所评论的,一个简单而优雅的解决方案是使用bignum,特别是如果内置,或者使用所选语言轻松提供。它对n,p,q没有限制。

我将在此处开发一种基于IEEE浮点的替代解决方案:

  • n,p,q完全可以用给定的浮点精度表示(例如,对于单个或双个IEEE 754,在24或53位内)
  • 可以使用融合乘法。

我会注意到f浮点类型,f(x)将值x转换为f,可能会舍入到最近的浮点数,与偶数相关联。

fsqrt(x)将表示精确平方根的浮点近似。

f x = fsqrt(f(n))f y = f(p) / f(q)

根据IEEE 754属性,x和y都是最接近精确结果的浮点数,而n=f(n)p=f(p)q=f(q)来自我们的初步条件。

因此,如果x < y问题得到解决sqrt(n) < p/q

如果x > y问题也解决了sqrt(n) > p/q

如果x == y我们无法立即告知......

让我们注意残留f r = fma(x,x,-n)f s = fma(y,q,-p)

我们完全有r = x*x - ns = y*q - p。因此s/q = y - p/q(确切的操作,而不是浮点操作)。

现在我们可以比较剩余误差。 (p/q)^2 = y^2-2*y*s/q+ (s/q)^2。它与n = x^2 - r相比如何?

n-(p/q)^2 = 2*y*s/q - r - (s/q)^2

因此,我们得到了差异d的近似值,在第一顺序:f d = 2*y*s/f(q) - r。所以这是一个类似C的原型:

int sqrt_compare(i n,i p,i q)
/* answer -1 if sqrt(n)<p/q, 0 if sqrt(n)==p/q, +1 if sqrt(n)>p/q */
/* n,p,q are presumed representable in f exactly */
{
  f x=sqrt((f) n);
  f y=(f) p / (f) q;
  if(x<y) return -1;
  if(x>y) return +1;
  f r=fma(x,x,-(f) n);
  f s=fma(y,(f) q,-(f) p);
  f d=y*s/(f) q - r;
  if(d<0) return -1;
  if(d>0) return +1;
  if(r==0 && s==0) return 0; /* both exact and equal */
  return -1; /* due to 2nd order */
}

正如你所看到的,它相对较短,应该是高效的,但很难破译,所以至少从这个POV来看,我不会认为这个解决方案比琐碎的bignum更好。

答案 1 :(得分:1)

您可以考虑使用大小为2x的整数解决方案3,

n * uint2n_t{q} * q <= uint2n_t{p} * p

如果n * q * q溢出,则会溢出,但在这种情况下,无论如何都会返回false。

uint2n_t nqq;
bool overflow = __builtin_mul_overflow(uint2n_t{n} * q, q, &nqq);
(!overflow) && (uint2n_t{n} * q * q <= uint2n_t{p} * p);