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
您可以将问题简化为采用浮点模 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。也许可以比依赖浮点减法做得更好。