我的问题是我必须使用thrid-party函数/算法,它将 double -precision值数组作为输入,但显然可能对输入数据中的非常小的变化敏感。但是对于我的应用程序,我必须获得相同的结果,以获得(几乎)相同的输入!特别是我有两个测试输入数组,它们在小数点后的第5个位置是相同的,但我仍然得到不同的结果。那么导致“问题”的原因必须是在小数点后的第5个位置。
现在我的想法是将输入舍入到稍低的精度,以便从非常相似的输入获得相同的结果,但不 100%相同。因此,我正在寻找一种好的/有效的方法来将双 - 精度值舍入到略微更低的精度。到目前为止,我使用此代码舍入到小数点后的第9个位置:
double x = original_input();
x = double(qRound(x * 1000000000.0)) / 1000000000.0;
这里qRound()是Qt的正常的double到整数舍入函数。这段代码有效,它确实通过两个“有问题”的测试集解决了我的问题。但是:有更有效的方法吗?
还有什么困扰我:对于-100.0到100.0范围内的输入数据,舍入到小数点后的第9个位置可能是合理的(就像我当前的输入数据一样)。但是,例如,对于-0.001到0.001范围内的输入数据,可能太多(即,太多精度损失)。不幸的是,我不知道在其他情况下我的输入值会在什么范围内......
毕竟,我认为我需要的是类似于执行以下功能的功能:通过适当的舍入,将给定的 double - 精度值X切断为最多LN小数点后的位置,其中L是 double -precision可以存储(表示)给定值的小数点后的位置数;和N是固定的,就像3 一样。这意味着对于“小”值,我们将允许小数点后的位置比“大”值更多。换句话说,我想将64位浮点值舍入到(稍微)更小的精度,如60位或56位,然后将其存储回64位双精度值。
这对你有意义吗?如果是这样,你能否建议一种在C ++中有效地做到这一点的方法???
提前致谢!
答案 0 :(得分:1)
如果你看一下double bit layout,你可以看到如何将它与一点点魔法相结合,以实现快速(二进制)舍入到任意精度。您有以下位布局:
SEEEEEEEEEEEFFFFFFFFFFF.......FFFFFFFFFF
其中S
是符号位,E
是指数位,F
是小数位。你可以制作这样的位掩码:
11111111111111111111111.......1111000000
和按位 - 和(&
)两者结合在一起。结果是原始输入的圆形版本:
SEEEEEEEEEEEFFFFFFFFFFF.......FFFF000000
您可以通过更改尾随零的数量来控制切断的数据量。更多的零=更圆;少=少。您还可以获得所需的其他效果:小输入值会按比例减小大输入值,因为每个位对应的“位置”由指数决定。
希望有所帮助!
警告:这在技术上是截断而不是真正的舍入(无论它们与其他可能的结果有多接近,所有值都将变得更接近于零),但是希望它在你的情况下同样有用。
答案 1 :(得分:1)
感谢您目前的投入。
然而经过一些搜索,我遇到了frexp()和ldexp()函数!这些函数使我可以访问给定 double 值的“尾数”和“指数”,也可以从尾数 + exponent 转换回来一个双。现在我只需要围绕尾数。
double value = original_input();
static const double FACTOR = 32.0;
int exponent;
double temp = double(round(frexp(value, &exponent) * FACTOR));
value = ldexp(temp / FACTOR, exponent);
我不知道这是否有效,但它给出了合理的结果:
0.000010000000000 0.000009765625000
0.000010100000000 0.000010375976563
0.000010200000000 0.000010375976563
0.000010300000000 0.000010375976563
0.000010400000000 0.000010375976563
0.000010500000000 0.000010375976563
0.000010600000000 0.000010375976563
0.000010700000000 0.000010986328125
0.000010800000000 0.000010986328125
0.000010900000000 0.000010986328125
0.000011000000000 0.000010986328125
0.000011100000000 0.000010986328125
0.000011200000000 0.000010986328125
0.000011300000000 0.000011596679688
0.000011400000000 0.000011596679688
0.000011500000000 0.000011596679688
0.000011600000000 0.000011596679688
0.000011700000000 0.000011596679688
0.000011800000000 0.000011596679688
0.000011900000000 0.000011596679688
0.000012000000000 0.000012207031250
0.000012100000000 0.000012207031250
0.000012200000000 0.000012207031250
0.000012300000000 0.000012207031250
0.000012400000000 0.000012207031250
0.000012500000000 0.000012207031250
0.000012600000000 0.000012817382813
0.000012700000000 0.000012817382813
0.000012800000000 0.000012817382813
0.000012900000000 0.000012817382813
0.000013000000000 0.000012817382813
0.000013100000000 0.000012817382813
0.000013200000000 0.000013427734375
0.000013300000000 0.000013427734375
0.000013400000000 0.000013427734375
0.000013500000000 0.000013427734375
0.000013600000000 0.000013427734375
0.000013700000000 0.000013427734375
0.000013800000000 0.000014038085938
0.000013900000000 0.000014038085938
0.000014000000000 0.000014038085938
0.000014100000000 0.000014038085938
0.000014200000000 0.000014038085938
0.000014300000000 0.000014038085938
0.000014400000000 0.000014648437500
0.000014500000000 0.000014648437500
0.000014600000000 0.000014648437500
0.000014700000000 0.000014648437500
0.000014800000000 0.000014648437500
0.000014900000000 0.000014648437500
0.000015000000000 0.000015258789063
0.000015100000000 0.000015258789063
0.000015200000000 0.000015258789063
0.000015300000000 0.000015869140625
0.000015400000000 0.000015869140625
0.000015500000000 0.000015869140625
0.000015600000000 0.000015869140625
0.000015700000000 0.000015869140625
0.000015800000000 0.000015869140625
0.000015900000000 0.000015869140625
0.000016000000000 0.000015869140625
0.000016100000000 0.000015869140625
0.000016200000000 0.000015869140625
0.000016300000000 0.000015869140625
0.000016400000000 0.000015869140625
0.000016500000000 0.000017089843750
0.000016600000000 0.000017089843750
0.000016700000000 0.000017089843750
0.000016800000000 0.000017089843750
0.000016900000000 0.000017089843750
0.000017000000000 0.000017089843750
0.000017100000000 0.000017089843750
0.000017200000000 0.000017089843750
0.000017300000000 0.000017089843750
0.000017400000000 0.000017089843750
0.000017500000000 0.000017089843750
0.000017600000000 0.000017089843750
0.000017700000000 0.000017089843750
0.000017800000000 0.000018310546875
0.000017900000000 0.000018310546875
0.000018000000000 0.000018310546875
0.000018100000000 0.000018310546875
0.000018200000000 0.000018310546875
0.000018300000000 0.000018310546875
0.000018400000000 0.000018310546875
0.000018500000000 0.000018310546875
0.000018600000000 0.000018310546875
0.000018700000000 0.000018310546875
0.000018800000000 0.000018310546875
0.000018900000000 0.000018310546875
0.000019000000000 0.000019531250000
0.000019100000000 0.000019531250000
0.000019200000000 0.000019531250000
0.000019300000000 0.000019531250000
0.000019400000000 0.000019531250000
0.000019500000000 0.000019531250000
0.000019600000000 0.000019531250000
0.000019700000000 0.000019531250000
0.000019800000000 0.000019531250000
0.000019900000000 0.000019531250000
0.000020000000000 0.000019531250000
0.000020100000000 0.000019531250000
毕竟似乎喜欢我所寻找的东西:
http://img833.imageshack.us/img833/9055/clipboard09.png
现在我只需要为我的功能找到一个好的FACTOR值....
有任何意见或建议吗?
答案 2 :(得分:1)
业务情景在问题中并不明显;仍然我觉得你试图看到这些值在可接受的范围内。而不是==,您可以检查第二个值是否在某个%范围内(比如+/- 0.001%)
如果范围百分比无法修复(平均值,根据精确长度而变化;例如,对于2位小数,0.001%是好的,但需要4位小数0.000001%),那么,您可以通过1 /尾数到达它。
答案 3 :(得分:0)
我知道这个问题已经很老了,但是我也寻找一种方法来将double
值四舍五入到较低的精度。也许,这个答案可以帮助某个人。
想象一下以二进制表示的浮点数。例如1101.101
。 1101
位代表数字的整数部分,并从左到右分别用2^3
,2^2
,2^1
,2^0
加权。小数部分的101
位用2^-1
,2^-2
,2^-3
加权,等于1/2
,1/4
,{{1} }。
那么当您在小数点后两位截断数字时会产生十进制误差吗?在本示例中,该位为1/8
,因为该位置1。如果不设置该位,则错误为0.125
。因此,错误为0
。
现在以一种更一般的方式思考:如果您有一个无限长的尾数,那么小数部分会收敛到1(see here)。实际上,您只有52位(see here),因此总和为“几乎”1。因此,切掉所有小数位将导致错误<= 0.125
,这并不令人感到意外! (请记住,您的整数部分还占用了尾数空间!但是,如果您假设像<= 1
这样的数字以二进制表示形式是1.5
,则尾数只存储小数点后的部分。)< / p>
由于切掉所有小数位会导致1.1
的错误,所以切掉小数点右边除第一个位之外的所有其他位都会导致<= 1
的错误,因为此位的权重为{{1} }。再进一步,将您的错误减少到<= 1/2
。
这可以用函数2^-1
来描述,其中<= 1/4
是从右边开始计数的截止位数,f(x) = 1/2^(52-x)
是所产生错误的上限。
将小数点后的两位四舍五入意味着将数字按公分百分。可以使用上面的功能x
完成。这意味着您切除x位时所产生的错误以百分之一为界。用x求解不等式可得出:y = f(x)
,其中1/100 >= 1/2^(52-x)
是52-log2(100) >= x
。这意味着切除不超过52-log2(100)
位可确保浮点后的两个小数点(!)位置具有“精度”。
通常,尾数由整数和小数部分组成。我们称它们的长度为45.36
和45
。正指数描述i
。而且f
成立。上述不等式的解决方案更改为:i
,因为小数部分结束后,您必须停止截取尾数! (52=f+i
是十进制精度。)
应用对数规则,您可以像这样计算出允许截断的最大允许位数:
52-i-log2(10^n) >= x
,其中常量表示n
。然后可以使用以下方式完成截断:
x = f - (uint16_t) ceil(n / 0.3010299956639812);
如果log10(2)
大于mantissa >>= x;
mantissa <<= x;
,请记住仅移动x
。否则,会影响尾数的组成部分。