【发布时间】:2015-06-09 00:45:03
【问题描述】:
我正在使用 Mono 在 C# 中实现一些 32 位浮点三角函数,希望使用 Mono.Simd。我目前只缺少可靠的范围缩减。 我现在很困惑,因为显然Mono's SIMD extensions 不包括浮点数和整数之间的转换,这意味着我无法使用通常方法的舍入/截断。但是,我可以在整数和浮点数之间进行按位转换。
这样的事情可以做吗?如果需要,我可以放大和缩小域,但理想情况下,范围缩小应该导致域为 [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);
}
编辑 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
我们可以看到在所有情况下的输出都是一个新数字,没有原始指数,尾数移动了一个数量(基于指数和第一个指数之后尾数的第一个非零位-bits 的尾数被忽略)到指数中。但我不确定这是否是正确的方法,它只是在纸上效果很好。
编辑3: 我卡在 Mono 版本 2.0.50727.1433
【问题讨论】:
-
我对域的含义感到困惑。您谈论的是 [-pi,pi] 的域,但是您的域变量是一个 int,而不是浮点数或双精度数的间隔。你想计算像 100 mod 2pi 这样的东西吗?
-
是的。我在谈论不同的东西,因为我很绝望并尝试了各种方法(x mod 2pi = x - (int)(x / (2*pi)) * 2 * pi),因此可以通过整数模拟域.
-
那么,您希望能够仅通过位操作计算 100 mod 2pi 之类的东西,而不能执行浮点除法或强制转换之类的操作吗?
-
我有 reinterpret/bit casts、float 和 int 算术 (/ * + -) 以及位操作 (| & ~ >),但没有本地转换(floor、int casts) .您可以在顶部的链接中查看可用操作的完整文档。