管理 C++ 单精度和双精度混合计算的规则是啥?

Posted

技术标签:

【中文标题】管理 C++ 单精度和双精度混合计算的规则是啥?【英文标题】:What are the rules governing C++ single and double precision mixed calculations?管理 C++ 单精度和双精度混合计算的规则是什么? 【发布时间】:2011-05-13 11:47:15 【问题描述】:

例如,这些变量:

result (double)
a (double)
b (float)
c (float)
d (double)

一个简单的计算:

result = a * (b + c) * d

类型转换的方式和时间以及如何确定每次计算的执行精度?

【问题讨论】:

Order of commutative mathematical operations、C/C++ Math Order of Operation、What are the rules governing C++ single and double precision mixed calculations?、Order of operations to maximize precision 等可能重复 【参考方案1】:

如果你有:

float f;
double d;

...然后像f * d 这样的算术表达式会将两个操作数提升为更大的类型,在本例中为double

因此,表达式a * (b + c) * d 的计算结果为double,然后存储在result 中,这也是double。这种类型提升是为了避免意外的精度损失。

如需了解更多信息,请阅读这篇关于常用arithmetic conversions 的文章。

【讨论】:

但是 plus 的两个操作数都是浮点数。所以加法将使用浮点数完成,其结果将在乘法运算完成之前升级为双精度数。【参考方案2】:

浮点数将向上转换为双精度数。显式转换值。

即,如果你想要双倍的结果,你会写:

result = a * double( b + c ) * d;

总是值得明确的。它会引起这样的误解,任何尝试使用您的代码的人都可以立即明白您的意思。

【讨论】:

这是不正确的。在原始表达式中,bc 都不会被提升为 double,它们将被添加为两个 float 。仅当与已经是 double 的值相乘时,结果才会提升为 double,即 a @Martin:如上所述。我的错误:(【参考方案3】:

你有括号分隔浮动加法。 所以它会将 b + c 做为 float + float。将其转换为双精度以保持最大精度,然后将双精度值相乘。

但是,在您希望控制转化而不是猜测的情况下: 使用static_cast<>();

【讨论】:

【参考方案4】:

所有操作都是在相同类型的对象上完成的(假设是正常的算术运算)。

如果您编写的程序使用不同的类型,那么编译器会自动升级 ONE 参数,使它们都相同。

在这种情况下,浮点数将升级为双精度数:

result      = a * (b + c) * d

float  tmp1 = b + c;            // Plus operation done on floats.
                                // So the result is a float

double tmp2 = a * (double)tmp1; // Multiplication done on double (as `a` is double)
                                // so tmp1 will be up converted to a double.

double tmp3 = tmp2 * d;         // Multiplication done on doubles.
                                // So result is a double

result      = tmp3;             // No conversion as tmp3 is same type as result.

【讨论】:

还要注意未指定的常量被假定为双精度所以 float a; a=a+2.5;要将a转换为double,执行add然后转换回single。 a=a+2.5F;将迫使它进行所有单一的数学运算。【参考方案5】:

按照操作顺序,每个子表达式都被转换为它的类型(不确定这里的术语,可能是主要的?)类型。 double 优于 float,所以:

(b + c) // this is evaluated as a float, since both b and c are floats
a * (b + c) // this is evaluated as a double, since a is a double
a * (b + c) * d // this is evaluated as a double, since both "a * (b + c)" and d are doubles

【讨论】:

【参考方案6】:

在您的示例中,当计算右侧公式时,所有 float 类型都被类型提升为 double

至于它们是如何转换的:我所读到的关于浮点运算的内容是,大多数现代硬件在特殊硬件寄存器中使用扩展精度(80 位)长双精度来执行 FP 操作(至少我记得这是现代英特尔 x86/x87 处理器)。据我了解,floatdouble 通过特殊的 FP 指令在硬件中进行类型提升(如果我错了,请有人纠正我)。

【讨论】:

这是不正确的。 float 不会自动提升为 double。仅当它们出现在算术运算中时,它们才被转换为 double,而某些东西已经是 double。在这种情况下,bc 在添加之前都不会转换为 double【参考方案7】:

您必须区分类型转换和值转换。 C++ 标准(也包括 C)允许以扩展精度进行浮点计算。

“浮动操作数的值和浮动表达式的结果可以用比类型要求更高的精度和范围来表示;类型不会因此而改变。”

作为类型,b + c 是两个浮点数的加法。结果是一个浮点数。然后将结果类型提升为双精度,并且两次乘法作为双精度进行,结果为双精度。

但是,允许实现使用双精度(或更高精度)进行所有计算,包括 b + c。 事实上,我使用 Visual C++ 进行了尝试,它使用 x86 上可用的 80 位浮点堆栈完成了所有计算。

【讨论】:

这里唯一真正正确的答案:结果,'a' 和 'd' 是 double 我们知道这些计算至少是用双精度完成的。但“a + b”部分可以是 32 位、64 位或 80 位。这甚至会导致非确定性代码:将浮点“a”与直接复制“b”进行比较可能会产生不同的结果。如果 a 刚刚计算并且仍在寄存器中,它可能是 64/80 位,而副本“b”只有 32 位 - 比较失败。并且稍后进行 2 行相同的比较可能会显示数字现在相等,因为两者都是从内存而不是寄存器加载的。

以上是关于管理 C++ 单精度和双精度混合计算的规则是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用浮点数和双精度时,c 中的 -0.0000 是啥?

浮点型是啥意思

如何在 C++ 中的同一函数中使用字符串和双精度数

浮点数中单精度和双精度的编码表示

将(n个第一个字节)无符号字符指针转换为浮点数和双精度C++

单精度与双精度的区别