C ++中的浮点错误

时间:2012-02-05 10:56:38

标签: c++ floating-point

我需要一些必须准确的浮动分区,就像它们的双重版本一样。我可以改变分割值 - 它代表一个映射,我可以抵消它 - 来纠正最终的浮点错误。

为了纠正错误,我使用了以下代码:

do 
{
    float fValue = float(x) / 1024.f;
    double oldFValue = fValue;
    double dValue = double(x) / 1024.0;
    if(oldFValue != dValue)
    {
        x += 1;
    }
    else
    {
        break;
    }
}while(1);

使用此代码,

x = 11 

我有调试器(Visual Studio 2010):

fValue = 0.010742188
oldFValue = 0.010742187500000000

您能否解释为什么可移动值与浮动值不同?这是调试器问题还是浮点转换问题?我问这个是因为:

if(oldFValue != dValue)

永远不会是真的,即使它应该是。我可以用其他方式比较浮点值和双值吗?我需要浮点除法的结果与双除法完全相同。

5 个答案:

答案 0 :(得分:10)

答案 1 :(得分:2)

您对single precision float了解多少?

它存储为<sign><exponent><mantis>。您可以将最终号码写为:

(sign ? 1 : -1) * 0.1<mantis> * 2^(expontent - 127)

正如您所见,数字始终存储为数字>1和二进制分数。不幸的是,0.1 dec这样的某些数字是二进制的,因此您不会使用float获得精确的结果。

您可以尝试使用此功能:if(oldFValue != (float)dValue)如果它不能正常工作,您也可以尝试:

if(oldFValue*32 != (float)dValue*32)

这将导致:

mantis >> 5
expontent += 5

这可能会消除您的错误(尝试1(很奇怪,但在某些情况下可能有效),2,4,8,16 ......,2 ^ n)。

编辑:明确阅读Johnsywebs link

答案 2 :(得分:2)

11/1024在floatdouble中都可以完全表示。当然是oldFValue == dValue

答案 3 :(得分:1)

浮点问题在于任何非单一范围都存在无限数量的有理数。但是,您只有有限位数来表示您的浮点数。

因此 - 浮点数不是实数/有理数 - 并且表现不同。你应该期望它不是一个真实的数字。

因此,您永远不应使用operator==检查浮点的相等性。你应该计算delta=abs(num1-num2),并检查它是否小于你可以容忍其错误的某个值。

正如@Johnsyweb所说,阅读和理解所附文章对于正确处理浮点非常重要。

答案 4 :(得分:1)

大多数浮点运算都是在尾数数据丢失的情况下执行的,即使组件很好地适合它(数字如0.5或0.25)。 例如

a + c + b

不同
#include <stdio.h>

// Helpers declaration, for implementation scroll down
float getAllOnes(unsigned bits);
unsigned getMantissaBits();

int main() {

  // Determine mantissa size in bits
  unsigned mantissaBits = getMantissaBits();

  // Considering mantissa has only 3 bits, we would then get:
  // a = 0b10   m=1,  e=1
  // b = 0b110  m=11, e=1
  // c = 0b1000 m=1,  e=3
  // a + b = 0b1000, m=100, e=1
  // a + c = 0b1010, truncated to 0b1000, m=100, e=1
  // a + b + c result: 0b1000 + 0b1000 = 0b10000, m=100, e=2
  // a + c + b result: 0b1000 + 0b110 = 0b1110, m=111, e=1

  float a = 2,
        b = getAllOnes(mantissaBits) - 1,
        c = b + 1;

  float ab = a + b;
  float ac = a + c;

  float abc = a + b + c;
  float acb = a + c + b;

  printf("\n"
         "FP partial invariance issue demo:\n"
         "\n"
         "Mantissa size = %i bits\n"
         "\n"
         "a = %.1f\n"
         "b = %.1f\n"
         "c = %.1f\n"
         "(a+b) result: %.1f\n"
         "(a+c) result: %.1f\n"
         "(a + b + c) result: %.1f\n"
         "(a + c + b) result: %.1f\n"
         "---------------------------------\n"
         "diff(a + b + c, a + c + b) = %.1f\n\n",
         mantissaBits,
         a, b, c,
         ab, ac,
         abc, acb,
         abc - acb);

  return 1;
}

// Helpers

float getAllOnes(unsigned bits) {
    return (unsigned)((1 << bits) - 1);
}

unsigned getMantissaBits() {

    unsigned sz = 1;
    unsigned unbeleivableHugeSize = 1024;
    float allOnes = 1;

    for (;sz != unbeleivableHugeSize &&
          allOnes + 1 != allOnes;
          allOnes = getAllOnes(++sz)
          ) {}

    return sz-1;
}

多个操作组件的顺序也很重要。

为了解决必要的问题,了解机器如何表示fp数字。

也许这会有所帮助: http://stepan.dyatkovskiy.com/2018/04/machine-fp-partial-invariance-issue.html

以下是+ b + c问题的C示例。祝你好运!

example.c

[33.33, 33.33, 33.33]