在C / C ++中通过双精度运算保证浮点数是否保留?

假设符合IEEE-754标准 ,是一个保证通过双重运输保留的浮法?

换句话说,下面的断言总会被满足吗?

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

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

根据IEEE的说法,是否有这样的情况,即assert会得到满足,但是在通过双重传输之后,确切的比特级表示不会被保留下来?

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

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

floattypes的值的集合是doubletypes的值集合的子集

而其他所有的C和C ++标准都是相同的。 据我所知,NaN和infinities是“ floattypes的值”,虽然值作为操作数使用了一些特殊的规则。

浮点数 – >双精度 – >浮点数转换恢复浮点数的原始值(一般情况下)是由于数值转换全部在目标types中可表示的情况下保留该值的事实。

比特级表示是一个稍微不同的问题。 想象一下, float值有两个不同的按位表示。 那么在C标准中没有任何东西可以防止浮动 – >双 – >浮动转换从一个切换到另一个。 在IEEE中,除非有填充位,否则不会发生“实际值”,但是我不知道IEEE是否排除了具有不同的按位表示的单个NaN。 NaNs不会和自己等同,所以也没有标准的方法来判断两个NaN是否是“相同的NaN”或“不同的NaN”,除了将它们转换成string。 这个问题可能是没有意义的。

有一点需要注意的是编译器的不一致模式,在这种模式下它们保留了“隐藏”的超精确值,例如中间结果保留在浮点寄存器中,并且没有四舍五入地重复使用。 我不认为这会导致你的示例代码失败,但只要你做了浮点==这是你开始担心的事情。

从C99开始:

6.3.1.5实际浮动types
1当浮动提升为double或long double,或者double提升为double时,其值不变。
2当一个double降级为float时,一个long double被降级为double或者float,或者一个以比它的语义types(见6.3.1.8)所要求的更高的精度和范围表示的值被明确地转换为它的语义typesif正在转换的价值可以准确地代表新的types,它是不变的…

我认为,这可以保证你的float-> double-> float转换将保留原来的float值。

该标准还定义了7.12 Mathematics <math.h>的macrosINFINITYNAN

4macrosINFINITY扩展为浮点types的常量expression式,表示正或无符号无穷大(如果可用); 否则就是在翻译时溢出的floattypes的正常数。
5当且仅当实现支持floattypes的安静NaN时,才定义macrosNAN。 它扩展到代表安静的NaN的floattypes的常量expression式。

所以,有这样的特殊价值的规定,转换可能也适用于他们(包括负无穷和负零)。

这个断言在flush-to-zero和/或denormalized-is-zero模式下会失败(例如用-mfpmath = sse,-fast-math等编译的代码,但是在编译器和体系结构上也是如此,例如Intel C ++编译器)如果f是非规范化的。

尽pipe如此,您不能在该模式下生成非规格化的浮点数,但情况仍然有可能:

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); }