当通过C/C++中的double进行运输时,是否保证保留浮子

Is a float guaranteed to be preserved when transported through a double in C/C++?

本文关键字:是否 保留 运输 C++ double 中的      更新时间:2023-10-16

假设符合IEEE-754,在通过双绞线传输时,是否保证保留浮点?

换言之,以下断言是否总是得到满足?

int main()
{
    float f = some_random_float();
    assert(f == (float)(double)f);
}

假设f可以获得IEEE定义的任何特殊值,如NaN和Infinity。

根据IEEE,是否存在这样的情况:断言将得到满足,但在通过double传输后,没有保留确切的比特级表示?

该代码段在C和C++中都是有效的。

您甚至不需要假设IEEE。C89在3.1.2.5中说:

类型float的值集是该值集的子集类型为double

其他所有的C和C++标准都有相同的内容。据我所知,NaN和无穷大是"float类型的值",尽管这些值在用作操作数时有一些特殊的大小写规则。

float->double->float转换恢复float的原始值这一事实(通常)源于这样一个事实,即如果值在目标类型中可表示,则数字转换都会保留该值。

比特级表示则略有不同。假设float的值有两个不同的逐位表示。然后,C标准中没有任何内容阻止float->double->float转换从一个转换到另一个。在IEEE中,除非有填充位,否则"实际值"不会发生这种情况,但我不知道IEEE是否排除了具有不同位表示的单个NaN。NaN与它们本身的比较并不相等,因此除了将它们转换为字符串之外,也没有标准的方法来判断两个NaN是"相同的NaN"还是"不同的NaN。这个问题可能没有实际意义。

需要注意的一点是编译器的不一致模式,在这种模式下,它们将超精确值"隐藏"起来,例如,中间结果留在浮点寄存器中,并在不取整的情况下重复使用。我不认为这会导致您的示例代码失败,但一旦您执行浮点==,就会开始担心这种事情。

来自C99:

6.3.1.5实际浮动类型
1当浮点被提升为双精度或长双精度,或双精度被提升为长双精度时,其值不变
2当double被降级为float,长double被降为double或float,或者以比其语义类型(见6.3.1.8)要求的更高精度和范围表示的值被显式转换为其语义类型时,如果被转换的值可以在新类型中准确地表示,则它是不变的。。。

我认为,这保证了float->double->float转换将保留原始float值。

该标准还定义了7.12 Mathematics <math.h>:中的宏INFINITYNAN

4宏INFINITY扩展为float类型的常量表达式,表示正无穷大或无符号无穷大(如果可用);否则转换为在转换时溢出的float类型的正常量
5当且仅当实现支持浮点类型的安静NAN时,才定义宏NAN。它扩展为float类型的常量表达式,表示一个安静的NaN。

因此,有这种特殊值的规定,转换可能也适用于它们(包括负无穷大和负零)。

如果f被反规范化,断言将在刷新为零和/或反规范化为零模式下失败(例如,使用mfpmath=sse、-fast math等编译的代码,但也可以在默认的编译器和体系结构堆上编译,如英特尔的C++编译器)。

尽管在该模式下无法生成非规范化浮点,但这种情况仍然存在:

a) 非规范化浮点来自外部源。

b) 有些库篡改FPU模式,但在每次函数调用后忘记(或故意避免)将其设置回,这使得调用方可能不匹配规范化。

打印以下内容的实际示例:

f = 5.87747e-39
f2 = 5.87747e-39
f = 5.87747e-39
f2 = 0
error, f != f2!

该示例适用于VC2010和GCC 4.3,但假设VC默认使用SSE进行数学运算,GCC默认使用FPU进行数学运算。否则,这个例子可能无法说明问题。

#include <limits>
#include <iostream>
#include <cmath>
#ifdef _MSC_VER
#include <xmmintrin.h>
#endif
template <class T>bool normal(T t)
{
    return (t != 0 || fabsf( t ) >= std::numeric_limits<T>::min());
}
void csr_flush_to_zero()
{
#ifdef _MSC_VER
    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
#else
    unsigned csr = __builtin_ia32_stmxcsr();
    csr |= (1 << 15);
    __builtin_ia32_ldmxcsr(csr);
#endif
}
void test_cast(float f) 
{
    std::cout << "f = " << f << "n";
    double d = double(f);
    float f2 = float(d);
    std::cout << "f2 = " << f2 << "n";
    if(f != f2)
        std::cout << "error, f != f2!n";
    std::cout << "n";
}
int main()
{
    float f = std::numeric_limits<float>::min() / 2.0;
    test_cast(f);
    csr_flush_to_zero();
    test_cast(f);
}