浮点舍入误差会将报告结果移动到范围内

时间:2017-12-22 10:43:12

标签: c floating-point rounding floating-accuracy

我正在开发一个函数来报告测试结果以及该特定测试结果的下限和上限。这三个值将使用指定的公式(aX + b)/ c进行转换,其中X是testResult / lowerLimit / upperLimit,a,b和c是浮点数。

如果报告的测试结果在转换之前在指定限制之内/之外,则在转换之后也应在限制之内/之外,以确保报告结果的有效性。

我已经确定了两种情况,其中无效的测试结果将在转换后在范围内移动但我还没有找到测试结果在转换之前的范围内并且在转换之后将超出指定限制的情况。这种情况甚至会发生吗?我不相信吗?可以吗?

下面是一些代码,它产生了我提到的两个案例以及更正,以确保报告的测试结果的有效性。

TLDR:下面代码中的((TRUE == insideLimitBefore)&&(FALSE == insideLimitAfter))案例会发生吗?

#include <stdio.h>
#include <stdint.h>

#define TRUE    (uint8_t)0x01
#define FALSE   (uint8_t)0x00

int32_t LinearMapping(const int32_t input);
void Convert(int32_t testResult, int32_t lowerLimit, int32_t upperLimit);

int main(void)
{

    int32_t lowerLimit = 504;
    int32_t testResult = 503;
    int32_t upperLimit = 1000;

    printf("INPUT:\n\tLower limit:\t%d\t\n\tTest result:\t%d\t\n\tUpper limit:\t%d\t\n", lowerLimit, testResult, upperLimit);
    Convert(testResult, lowerLimit, upperLimit);

    lowerLimit = 500;
    testResult = 504;
    upperLimit = 503;

    printf("INPUT:\n\tLower limit:\t%d\t\n\tTest result:\t%d\t\n\tUpper limit:\t%d\t\n", lowerLimit, testResult, upperLimit);
    Convert(testResult, lowerLimit, upperLimit);

    return 0;
}

int32_t LinearMapping(const int32_t input)
{
    float retVal;

    const float a = 1.0;
    const float b = 1.0;
    const float c = 2.3;

    retVal = a * input;
    retVal += b;
    retVal /= c;

    return (int32_t)retVal;
}

void Convert(int32_t testResult, int32_t lowerLimit, int32_t upperLimit)
{
    uint8_t insideLimitAfter;
    uint8_t belowLowerLimit;
    uint8_t insideLimitBefore = ((lowerLimit <= testResult) && (testResult <= upperLimit)) ? TRUE : FALSE;

    if (FALSE == insideLimitBefore)
    {
        /* testResult is either below or above lowerLimit/upperLimit respectively */
        if (testResult < lowerLimit)
        {
            belowLowerLimit = TRUE;
        }
        else /* testResult > upperLimit */
        {
            belowLowerLimit = FALSE;
        }
    }

    testResult = LinearMapping(testResult);
    lowerLimit = LinearMapping(lowerLimit);
    upperLimit = LinearMapping(upperLimit);

    insideLimitAfter = ((lowerLimit <= testResult) && (testResult <= upperLimit)) ? TRUE : FALSE;

    if ((FALSE == insideLimitBefore) && (TRUE == insideLimitAfter))
    {
        if (TRUE == belowLowerLimit)
        {
            printf("OUTPUT:\n\tLower limit:\t%d\t\n\tTest result:\t%d\t\n\tUpper limit:\t%d\t\n", lowerLimit+1, testResult, upperLimit);
        }
        else /* belowLowerLimit == FALSE => testResult > upperLimit */
        {
            printf("OUTPUT:\n\tLower limit:\t%d\t\n\tTest result:\t%d\t\n\tUpper limit:\t%d\t\n", lowerLimit, testResult, upperLimit-1);
        }
    }
    else if ((TRUE == insideLimitBefore) && (FALSE == insideLimitAfter))
    {
       /* Is this case even possible? */
    }
    else
    {
        /* Do nothing */
    }
}

1 个答案:

答案 0 :(得分:3)

  

查找测试结果在转换前的范围内并且在转换后超出指定限制的情况。这种情况甚至会发生吗?

不,鉴于理智a,b,c, lowerLimit, testResult, upperLimit

lo,x,hi线性转换之前,lo <= x <= hi给出3 LinearMapping() lo_new <= x_new <= hi_new,只要转换为(正)线性,a将保持相同的关系(没有除以0,bcfloat不是非数字)。没有转化超出int32_t范围的x

主要原因是[lo...hi]内部或极限LinearMapping()的边缘情况,x可能会降低所有3的有效精度。新{{1} }现在可能等于lohi==有利于&#34;在范围&#34;中。所以lo <= x <= hi没有变化。

OP最初发现&#34;无效测试结果的例子将在转换后移动到范围内&#34;因为x位于[lo...hi]之外,现在有效精确度降低x等于lohi。由于==偏爱&#34;在范围内,可以看到从外到内的移动。

注意:如果LinearMapping()的负斜率为-1,则lo <= x <= hi很容易被破坏。作为1 <= 2 <= 3 - &gt; -1 > -2 > -3。这使lowerLimit > upperLimit在&#34;范围内&#34;不能满足任何x

作为参考,OP的代码已经简化:

#include <stdio.h>
#include <stdint.h>

int LinearMapping(const int input) {
  const float a = 1.0;
  const float b = 1.0;
  const float c = 2.3;
  float retVal = a * input;
  retVal += b;
  retVal /= c;
  return (int) retVal;
}

void Convert(int testResult, int lowerLimit, int upperLimit) {
  printf("Before %d %s %d %s %d\n", lowerLimit,
      lowerLimit <= testResult ? "<=" : "> ", testResult,
      testResult <= upperLimit ? "<=" : "> ", upperLimit);
  testResult = LinearMapping(testResult);
  lowerLimit = LinearMapping(lowerLimit);
  upperLimit = LinearMapping(upperLimit);
  printf("After  %d %s %d %s %d\n\n", lowerLimit,
      lowerLimit <= testResult ? "<=" : "> ", testResult,
      testResult <= upperLimit ? "<=" : "> ", upperLimit);
}

int main(void) {
  Convert(503, 504, 1000);
  Convert(504, 500, 503);
  return 0;
}

输出

Before 504 >  503 <= 1000
After  219 <= 219 <= 435

Before 500 <= 504 >  503
After  217 <= 219 <= 219