32位十进制数的浮点/双精度分析
从另一个人的.c文件中,我看到了这个:
const float c = 0.70710678118654752440084436210485f;
他想避免计算sqrt(1/2)
。
这可以用普通的C/C++
以某种方式存储吗? 我的意思是没有失去精确度。 对我来说似乎不可能。
我正在使用C ++,但我不相信这两种语言之间的精确差异太大(如果有的话),那就是为什么我没有测试它。
所以,我写了几行,看看代码的行为:
std::cout << "Number: 0.70710678118654752440084436210485\n"; const float f = 0.70710678118654752440084436210485f; std::cout << "float: " << std::setprecision(32) << f << std::endl; const double d = 0.70710678118654752440084436210485; // no f extension std::cout << "double: " << std::setprecision(32) << d << std::endl; const double df = 0.70710678118654752440084436210485f; std::cout << "doublef: " << std::setprecision(32) << df << std::endl; const long double ld = 0.70710678118654752440084436210485; std::cout << "l double: " << std::setprecision(32) << ld << std::endl; const long double ldl = 0.70710678118654752440084436210485l; // l suffix! std::cout << "l doublel: " << std::setprecision(32) << ldl << std::endl;
输出是这样的:
* ** *** vvv Number: 0.70710678118654752440084436210485 // 32 decimal digits float: 0.707106769084930419921875 // 24 >> >> double: 0.70710678118654757273731092936941 doublef: 0.707106769084930419921875 // same as float l double: 0.70710678118654757273731092936941 // same as double l doublel: 0.70710678118654752438189403651592 // suffix l
其中*
是float
的最后一个准确数字, **
是double
的最后一个准确数字, ***
是double
的最后一个准确数字。
double
的输出有32位十进制数,因为我已将std::cout
的精度设置为该值。
float
输出有24个,如下所示:
float has 24 binary bits of precision, and double has 53.
我希望最后一个输出与前一个输出相同,即f
后缀不会阻止数字变为double
。 我想当我写这篇文章时:
const double df = 0.70710678118654752440084436210485f;
会发生的事情是,首先数字变为float
,然后存储为double
精度数,因此在第24个十进制数字后,它有零,这就是double
精度停在那里的原因。
我对么?
从这个答案我发现了一些相关的信息:
float x = 0 has an implicit typecast from int to float. float x = 0.0f does not have such a typecast. float x = 0.0 has an implicit typecast from double to float.
[编辑]
关于__float128
,它不是标准的,因此它不在竞争中。 在这里查看更多。
从标准:
有三种浮点类型:float,double和long double。 double类型提供至少与float一样多的精度,long double类型提供至少与double一样多的精度。 float类型的值集是double类型的值集的子集; double类型的值集是long double类型的值集的子集。 浮点类型的值表示是实现定义的。
所以你可以在这个问题上看到你的问题:标准实际上没有说明精确浮点数是多少。
在标准实现方面,您需要查看IEEE754,这意味着Irineau和Davidmh的另外两个答案是解决问题的完美有效方法。
至于后缀字母表示类型,再看标准:
除非由su ffi x明确指定,否则浮点文字的类型是double。 su ffi xes f和F指定float,su ffi xes l和L指定long double。
因此,除非使用L
后缀,否则创建long double
的尝试将与分配给它的double
literal具有相同的精度。
我知道其中一些答案可能看起来不太令人满意,但在你可以解雇答案之前,有很多背景阅读需要在相关标准上完成。 这个答案已经比预期的要长,所以我不会试着在这里解释一切。
最后要注意的是:由于精度没有明确定义,为什么不能获得比它需要的更长的常数? 似乎有意义总是定义一个足够精确的常量,无论类型如何总是可以表示。
Python的数值库numpy具有非常方便的浮点信息function。 所有类型都相当于C:
对于C的浮点数:
print numpy.finfo(numpy.float32) Machine parameters for float32 --------------------------------------------------------------------- precision= 6 resolution= 1.0000000e-06 machep= -23 eps= 1.1920929e-07 negep = -24 epsneg= 5.9604645e-08 minexp= -126 tiny= 1.1754944e-38 maxexp= 128 max= 3.4028235e+38 nexp = 8 min= -max ---------------------------------------------------------------------
对于C的双倍:
print numpy.finfo(numpy.float64) Machine parameters for float64 --------------------------------------------------------------------- precision= 15 resolution= 1.0000000000000001e-15 machep= -52 eps= 2.2204460492503131e-16 negep = -53 epsneg= 1.1102230246251565e-16 minexp= -1022 tiny= 2.2250738585072014e-308 maxexp= 1024 max= 1.7976931348623157e+308 nexp = 11 min= -max ---------------------------------------------------------------------
对于C的长期浮动:
print numpy.finfo(numpy.float128) Machine parameters for float128 --------------------------------------------------------------------- precision= 18 resolution= 1e-18 machep= -63 eps= 1.08420217249e-19 negep = -64 epsneg= 5.42101086243e-20 minexp=-16382 tiny= 3.36210314311e-4932 maxexp= 16384 max= 1.18973149536e+4932 nexp = 15 min= -max ---------------------------------------------------------------------
因此,即使长浮点数(128位)也不会为您提供所需的32位数字。 但是,你真的需要它们吗?
一些编译器具有binary128
浮点格式的实现,由IEEE 754-2008标准化 。 例如,使用gcc,类型为__float128
。 该浮点格式具有大约34个十进制精度( log(2^113)/log(10)
)。
您可以使用Boost Multiprecision库来使用它们的包装器float128 。 该实现将使用本机类型(如果可用),或使用直接替换。
让我们用新的非标准类型__float128
扩展您的实验,使用最近的g ++(4.8):
// Compiled with g++ -Wall -lquadmath essai.cpp #include #include #include #include std::ostream& operator<<(std::ostream& out, __float128 f) { char buf[200]; std::ostringstream format; format << "%." << (std::min)(190L, out.precision()) << "Qf"; quadmath_snprintf(buf, 200, format.str().c_str(), f); out << buf; return out; } int main() { std::cout.precision(32); std::cout << "Number: 0.70710678118654752440084436210485\n"; const float f = 0.70710678118654752440084436210485f; std::cout << "float: " << std::setprecision(32) << f << std::endl; const double d = 0.70710678118654752440084436210485; // no f extension std::cout << "double: " << std::setprecision(32) << d << std::endl; const double df = 0.70710678118654752440084436210485f; std::cout << "doublef: " << std::setprecision(32) << df << std::endl; const long double ld = 0.70710678118654752440084436210485; std::cout << "l double: " << std::setprecision(32) << ld << std::endl; const long double ldl = 0.70710678118654752440084436210485l; // l suffix! std::cout << "l doublel: " << std::setprecision(32) << ldl << std::endl; const __float128 f128 = 0.70710678118654752440084436210485; const __float128 f128f = 0.70710678118654752440084436210485f; // f suffix const __float128 f128l = 0.70710678118654752440084436210485l; // l suffix const __float128 f128q = 0.70710678118654752440084436210485q; // q suffix std::cout << "f128: " << f128 << std::endl; std::cout << "f f128: " << f128f << std::endl; std::cout << "l f128: " << f128l << std::endl; std::cout << "q f128: " << f128q << std::endl; }
输出是:
* ** *** **** vvvv Number: 0.70710678118654752440084436210485 float: 0.707106769084930419921875 double: 0.70710678118654757273731092936941 doublef: 0.707106769084930419921875 l double: 0.70710678118654757273731092936941 l doublel: 0.70710678118654752438189403651592 f128: 0.70710678118654757273731092936941 f f128: 0.70710676908493041992187500000000 l f128: 0.70710678118654752438189403651592 q f128: 0.70710678118654752440084436210485
其中*
是float
的最后一个准确数字, **
double
的最后一个准确数字, ***
long double
的最后一个准确数字, ****
是__float128
的最后一个准确数字。
正如另一个答案所说,C ++标准没有说明各种浮点类型的精度是什么(就像它没有说明整数类型的大小一样)。 它仅指定这些类型的最小精度/大小。 但规范IEEE754确实指明了所有这些! 所有许多架构的FPU都实现了IEEE745的规范,最新版本的gcc实现了扩展名为__float128
的规范类型__float128
。
至于你的代码或我的代码的解释,像0.70710678118654752440084436210485f
这样的表达式是一个浮点字面值。 它有一个类型,由后缀定义,这里f
为float
。 因此,文字的值对应于给定数字中给定类型的最近值。 这解释了为什么,例如,“doublef”的精度与代码中的“float”的精度相同。 在最近的gcc版本中,有一个扩展,它允许定义类型为__float128
浮点文字,带有Q
后缀(四倍精度)。