【问题标题】:Changing equation to extend integer overflow更改方程式以扩展整数溢出
【发布时间】:2016-08-21 03:11:08
【问题描述】:

问题定义

我有一个由一组点 (x,y) 表示的二维场景,其中 x 和 y 是 64 位整数。 x 和 y 值都在 [0, R] 的范围内(可能的 x 最小值为 0,最大值为 R。同样的规则适用于 y)。

在我的代码中的某个时刻,我执行以下操作。

// Returns true if point P is inside circle defined by points A, B, C.
private bool InCircle(Long2 A, Long2 B, Long2 C, Long2 P)
{
    Long2 AP = P - A;
    Long2 BP = P - B;
    Long2 CP = P - C;
    return AP.SquareMagnitude * Long2.Det(BP, CP) + BP.SquareMagnitude * Long2.Det(CP, AP) + CP.SquareMagnitude * Long2.Det(AP, BP) > 0;
}

// Notes:
SquareMagnitude = x * x + y * y
Long2.Det(a, b) = a.x * b.y - a.y * b.x

我不希望这个操作失败,所以其中的整数不能溢出。最大值出现在以下情况:

  • A = (0, 0)
  • B = (R, 0)
  • C = (0, R)
  • P = (R, R)

这些点导致:

  • AP = (R, R)
  • BP = (0, R)
  • CP = (R, 0)

如果这些值在方法上运行,我们得到:

  • AP.SquareMagnitude = R² + R²
  • Lo​​ng2.Det(BP, CP) = R²
  • BP.SquareMagnitude = R²
  • Lo​​ng2.Det(CP, AP) = R²
  • CP.SquareMagnitude = R²
  • Lo​​ng2.Det(AP, BP) = R²

结果变成:

  • (2 * R² * R²) + (R² * R²) + (R² * R²) > 0

等同于:

  • 4 * (R^4) > 0

如果我们不希望 64 位整数溢出,那么:

  • 4 * (R^4)

因此,最大 R 值为 2^15 = 32768。(实际上是 2^15.25,但我们会将其四舍五入为 2^15)。


问题

有什么方法可以增加最大 R 值而不会使用 64 位整数溢出?比如把方程分解成更小的值,然后分别比较。


到目前为止我的想法

我想把等式改成:

return AP.SquareMagnitude * Long2.Det(BP, CP) > -BP.SquareMagnitude * Long2.Det(CP, AP) - CP.SquareMagnitude * Long2.Det(AP, BP);

这会导致:

  • 2 * R² * R² > -(R² * R²) - (R² * R²)
  • 2 * (R^4)
  • R

此外,我不知道这样做是否安全。是吗?

使用整数是有目的的,输出精度是必须的。

我知道这个库:

https://www.cs.cmu.edu/~quake/robust.html

但我想使用整数,而不是浮点数。这里使用的方法在整数溢出限制内是稳健的。只希望扩展此溢出限制。

【问题讨论】:

    标签: integer overflow long-integer biginteger integer-overflow


    【解决方案1】:

    所以我想运行检查整数算术,如果溢出,将 64 位整数转换为小数。

    // Returns true if point P is inside circle defined by points A, B, C.
    private bool InCircle(Long2 A, Long2 B, Long2 C, Long2 P)
    {
        Long2 AP = P - A;
        Long2 BP = P - B;
        Long2 CP = P - C;
        try 
        {
            long a = checked(AP.SquareMagnitude * Long2.Det(BP, CP));
            long b = checked(BP.SquareMagnitude * Long2.Det(CP, AP));
            long c = checked(CP.SquareMagnitude * Long2.Det(AP, BP));
            long lhs = checked(checked(a + b) + c);
            return lhs > 0;
        }
        catch (System.OverflowException)
        {
            decimal a1 = new decimal(AP.SquareMagnitude);
            decimal a2 = new decimal(Long2.Det(BP, CP));
            decimal b1 = new decimal(BP.SquareMagnitude);
            decimal b2 = new decimal(Long2.Det(CP, AP);
            decimal c1 = new decimal(CP.SquareMagnitude);
            decimal c2 = new decimal(Long2.Det(AP, BP);
            decimal lhs = a1 * a2 + b1 * b2 + c1 * c2;
            return lhs > 0;
        }
    }
    
    // Notes:
    SquareMagnitude = x * x + y * y
    Long2.Det(a, b) = a.x * b.y - a.y * b.x
    

    如果我没记错的话,小数可以精确地表示小于 2^96 的整数。所以为了避免精度损失和溢出:

    • 4 * (R^4)
    • R

    为了避免整数运算溢出,(R² + R²) 是最大运算:

    • R² + R²
    • 2 * R²
    • R

    使用这种方法,最大R值变成2^23,非常棒。

    另一个想法:如果方法不尝试执行检查整数算术,而是从一开始就将值转换为小数并进行小数数学运算,那么代码平均运行速度会更快吗?

    这行得通吗?我是不是做错了什么?

    【讨论】:

      猜你喜欢
      • 2021-10-14
      • 2014-03-29
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-02
      • 2021-04-12
      • 2019-12-03
      相关资源
      最近更新 更多