浮点范围缩小

Sha*_*ggi 5 c# mono sse simd ieee-754

我正在使用Mono在C#中实现一些32位浮点三角函数,希望使用Mono.Simd.我目前只缺少固体范围减少.我现在很困惑,因为Mono的SIMD扩展显然不包括浮点数和整数之间的转换,这意味着我无法进行舍入/截断,这是常用的方法.然而,我可以在int和float之间按位转换.

可以这样做吗?如果需要,我可以上下调整域,但理想情况下,范围减少应该导致域[0,2 pi]或[-pi,pi].我有一种预感,如果域名是2的幂,就可以用指数做一些IEEE魔法,但我真的不知道该怎么做.

编辑:好的,我已经尝试搞乱这个C代码,感觉就像我正处于某种边缘(它不起作用,但小数部分始终是正确的,至少在十进制/ base10中......) .核心原则似乎是获得你的域和输入指数之间的指数差异,并组成一个带有移位尾数和调整指数的新浮点数.但它不适用于负数,我不知道如何处理非幂2(或任何分数 - 事实上,除2以外的任何东西都不起作用!).

// here's another more correct attempt:
float fmodulus(float val, int domain)
{
    const int mantissaMask = 0x7FFFFF;
    const int exponentMask = 0x7F800000;

    int ival = *(int*)&val;

    int mantissa = ival & mantissaMask;
    int rawExponent = ival & exponentMask;
    int exponent = (rawExponent >> 23) - (129 - domain);
    // powers over one:
    int p = exponent;

    mantissa <<= p;
    rawExponent = exponent >> p;
    rawExponent += 127;
    rawExponent <<= 23;

    int newVal = rawExponent & exponentMask;
    newVal |= mantissa & mantissaMask;

    float ret = *(float*)&newVal;

    return ret;
}

float range_reduce(float value, int range )
{
    const int mantissaMask = 0x7FFFFF;
    const int exponentMask = 0x7F800000;

    int ival = *(int*)&value;
    // grab exponent:
    unsigned exponent = (ival & exponentMask) >> 23;
    // grab mantissa:
    unsigned mantissa = ival & mantissaMask;

    // remove bias, and see how much the exponent is over range/domain
    unsigned char erange = (unsigned char)(exponent - (125 + range));
    // check if sign bit is set - that is, the exponent is under our range
    if (erange & 0x80)
    {
        // don't do anything then.
        erange = 0;
    }

    // shift mantissa (and chop off bits) by the reduced amount
    int inewVal = (mantissa << (erange)) & mantissaMask;
    // add exponent, and subtract the amount we reduced the argument with
    inewVal |= ((exponent - erange) << 23) & exponentMask;

    // reinterpret
    float newValue = *(float*)&inewVal;
    return newValue;
    //return newValue - ((erange) & 0x1 ? 1.0f : 0.0f);
}

int main()
{
    float val = 2.687f;
    int ival = *(int*)&val;
    float correct = fmod(val, 2);
    float own = range_reduce(val, 2);

    getc(stdin);
}
Run Code Online (Sandbox Code Playgroud)

编辑2:

好吧,我真的试图用ieee二元系统来理解这一点.如果我们写这样的模数运算:

output = input % 2

[exponent] + [mantissa_bit_n_times_exponent]

3.5     = [2] + [1 + 0.5]                   ->[1] + [0.5]       = 1.5
4.5     = [4] + [0 + 0 + 0.5]               ->[0.5] + [0]       = 0.5
5.5     = [4] + [0 + 1 + 0.5]               ->[1] + [0.5]       = 1.5
2.5     = [2] + [0 + 0.5]                   ->[0.5] + [0]       = 0.5
2.25    = [2] + [0 + 0 + 0.25]              ->[0.25]            = 0.25
2.375   = [2] + [0 + 0 + 0.25 + 0.125]      ->[0.25] + [0.125]  = 0.375
13.5    = [8] + [4 + 0 + 1 + 0.5]           ->[1] + [0.5]       = 1.5
56.5    = [32] + [16 + 8 + 0 + 0 + 0 + 0.5] ->[0.5]             = 0.5
Run Code Online (Sandbox Code Playgroud)

我们可以看到在所有情况下的输出都是一个新的数字,没有原始指数,并且尾数移动了一个数量(这是基于指数和尾数的第一个指数位之后的尾数的第一个非零位)忽略)进入指数.但我不确定这是否是正确的方法,它只是在纸上很好地解决了.

编辑3:我坚持使用Mono版本2.0.50727.1433

Dou*_*are 1

您可以将问题简化为采用浮点模 1。为了简化这一点,您可以使用位运算计算浮点的下限,然后使用浮点减法。以下是这些操作的(不安全)C# 代码:

// domain is assumed to be positive
// returns value in [0,domain)
public float fmodulus(float val, float domain)
{
    if (val < 0)
    {
        float negative = fmodulus(-val, domain);
        if (domain - negative == domain)
            return 0;
        else
            return domain-negative;
    }

    if (val < domain)
        return val; // this avoids losing accuracy

    return fmodOne(val / domain) * domain;
}

// assumes val >= 1, so val is positive and the exponent is at least 0 
unsafe public float fmodOne(float val)
{
    int iVal = *(int*)&val;
    int uncenteredExponent = iVal >> 23;
    int exponent = uncenteredExponent - 127; // 127 corresponds to 2^0 times the mantissa
    if (exponent >= 23) 
        return 0; // not enough precision to distinguish val from an integer

    int unneededBits = 23 - exponent; // between 0 and 23
    int iFloorVal = (iVal >> unneededBits) << unneededBits; // equivalent to using a mask to zero the bottom bits of the mantissa
    float floorVal = *(float*)&iFloorVal; // convert the bit pattern back to a float

    return val-floorVal;
}
Run Code Online (Sandbox Code Playgroud)

例如,fmodulus(100.1f, 1) 为 0.09999847。100.1f 的位模式是

0 10000101 10010000011001100110011

FloorVal (100f) 的位模式为

0 10000101 10010000000000000000000

浮点减法给出接近 0.1f 的值:

0 01111011 10011001100110000000000

事实上,我很惊讶最后 8 位被清零。我认为只有 0.1f 的最后 6 位应该被替换为 0。也许可以比依赖浮点减法做得更好。