从 double 转换为 short 会降低 C 中的结果

Casting from double to short decreasing the result in C

我有一段代码正在计算一些东西。计算结果是双倍的。但是,当我尝试在投射后将其分配给短片时,它正在减少。比如double的计算结果是30.000000,short后就是29.

short calcDays(int a, double b, double c)
{
    double result = (double) (a* (b/c));   // gives 30.000000
    short days = (short) result; // gives 29
    return days;
}

我也试过将它转换为整数。同样的结果。

编辑:a 始终是 1 的倍数,最小值 1 最大值是 365 b 并且 c 始终是 .1 的倍数,最小值 1.0 最大值是 1000 abc 来自 UI 作为服务调用参数

给定 abc 的约束,我们可以计算期望的结果为 (short) (a * lround(10*b) / lround(10*c))(short) (a * b / c + .00005)。 (这当然要求结果可以简而言之。abc 的规定限制不能保证这一点。)

前者ab/c等价于a•10b/(10c),所以我们只需要证明这就是表达式的计算结果,包括算法不会出现舍入误差。我们知道 b 理想情况下是 .1 的倍数,所以 10*b 是一个整数。 lround(10*b) 找到这个整数,有效地纠正了将十进制数字转换为 double 格式时发生的任何错误。同样,lround(10*c) 找到 10*c 的理想值。 lround returns long 类型的值,所以乘法和除法都是用整数运算。此外,long 类型能够表示必要的范围。 (a * lround(10*b) 被限制为 3,650,000,而 long 最多可以表示至少 2,147,483,647。)所以乘法是精确的,除法截断我们想要的方式。

后者的证明如下。

以下假定 IEEE-754“double”格式用于 double。在包含 <float.h> 之后,#if DBL_MANT_DIG >= 53 为真就足够了。

将用户提供的字符串中的数字转换为 double 应该会产生一个误差最多为一个最小精度单位 (ULP) 的数字。这在 C 标准的多个地方都有建议(并且不清楚文本的某些部分是否打算要求这样做)。但是,假设我们的转换错误,错误高达 1024 ULP。

bc可以达到1000,其ULP为2−43,所以1024 ULP为2−33。因此,如果用户输入 bb 就是 b(1+eb ),其中|eb| ≤ 2−33,而c类似c(1+ec)。 a作为最大365的整数,当然就是用户输入的a

计算a * b时,结果为 ab(1+eb)(1+e0),其中e0是乘法引入的误差。在任何舍入模式下,|e0|小于 1 ULP。此时最大值为365,000,其中1个ULP为2−34.

然后除以c,最小值为1,结果为 ab(1+eb)(1+e0)/(c(1+ec))•(1+e1), 其中e1是除法引入的错误。同样,最大值为 365,000,因此 |e1| < 2−34.

重新排列,结果为ab/c • (1+eb)(1+e0)(1+e 1)/(1+ec)。当 eb 为 +2−33 时,我们可以很容易地看到误差的界限, e0e1 是 2−34ec为−2−33。一些使用计算器的工作告诉我们误差小于 3.5•10−10.

注意ab/c等于10ab/(10c) 并考虑后者。分子为整数,分母为不超过10000的整数。因此,最接近整数而不是整数的商是 .0001。并且舍入误差计算的最接近整数的结果小于 .0001 − 3.5•10−10。因此,如果我们将 .00005 添加到计算结果,它会将本应为整数(无舍入误差)的每个结果推到该整数之上,但不会将任何不是整数的结果推到下一个整数之上。因此,a * b / c + .00005 以下的整数是所需的结果,如果它在 short.

范围内,则转换为 short 会提供该整数