我正在阅读Stephen Prata的C Primer Plus,它介绍浮点数的第一种方式是谈论浮点数在某一点上的准确性。它专门说:“ C标准规定,浮点数必须能够表示至少六个有效数字...浮点数必须准确表示前六个数字,例如33.333333”
这对我来说很奇怪,因为它听起来像是浮点数,精确到六位数,但这是不正确的。 1.4存储为1.39999 ...,依此类推。您仍然有错误。
那么到底提供了什么?对于数字应该有多精确,是否存在临界点?
在C语言中,如果没有得到编译器警告,则不能在浮动中存储六个以上的有效数字,但是为什么呢?如果您要执行的数字超过六位数,那么看来准确无误。
下溢和次正规数部分使这一点更加混乱。如果您有一个最小的浮点数,然后将其除以10,那么得到的错误似乎就不正常了吗?它们似乎只是上面提到的常规舍入错误。
那么,为什么书中说的浮点数精确到六位数,并且次正规数与常规舍入误差有何不同?
答案 0 :(得分:6)
假设您有一个带有 q 有效数字的十进制数字:
d q −1 。 d q −2 d q −3 ... d 0 ,
,我们也将其设为浮点十进制数字,这意味着我们将其乘以十的幂:
d q −1 。 d q −2 d q −3 … d 0 •10 e 。
接下来,我们将此数字转换为float
。 float
中无法精确表示许多此类数字,因此我们将结果四舍五入到最接近的可表示值。 (如果出现平局,我们将四舍五入以使低位数字保持偶数。)结果(如果我们没有上溢或下溢)是某个浮点数 x 。通过浮点数的定义(在C 2018 5.2.4.2.2 3中),它由某个底数中的某个数字表示,该数字被该底数缩放为幂。假设它是第二个基数, x 是:
b p −1 。 b p −2 b p −3 … b 0 •2 p 。
接下来,我们将此float
x 转换回有效位数为 q 的十进制。同样,float
值 x 可能无法精确地表示为带有 q 数字的十进制数字,因此我们得到了一些可能的新数字:
n q −1 。 n q −2 n q −3 ... n 0 •10 m 。
事实证明,对于任何float
格式,都有一些数字 q ,这样,如果我们以十进制数字开头的字符仅限于 q 位数,则此往返转换的结果将等于原始数字。每个 q 个十进制数字在四舍五入到float
之后又返回到 q 十进制数字时,都会得出起始数字。
在2018 C标准中,第5.2.4.2.2条第12款告诉我们该数字 q 必须至少为6(C实现可能支持更大的值),并且C实现应该为其定义float.h
中的预处理器符号(在FLT_DIG
中。
因此,考虑到您的示例编号1.4,当我们将其转换为IEEE-754基本32位二进制格式的float
时,我们得到的精确值为1.39999997615814208984375(这是其数学值,为方便起见以十进制显示;对象中的实际位用二进制表示)。当我们将其完全转换为十进制时,将得到“ 1.39999997615814208984375”。但是,如果将其转换为四舍五入的十进制十进制数,则得到“ 1.40000”。因此1.4在往返中幸存下来。
换句话说,通常{strong>不正确可以在float
中表示六个十进制数字而没有变化,但是float
确实带有足够的信息,您可以从中恢复六个小数位。
当然,一旦开始进行算术运算,错误通常会加重,并且您不再可以依靠六个十进制数字。
答案 1 :(得分:3)
感谢Govind Parmar引用了C11(或就此而言为C99)的在线示例。
您所指的“ 6”是“ FLT_DECIMAL_DIG”。
http://c0x.coding-guidelines.com/5.2.4.2.2.html
十进制数字n,这样任何浮点数都带有 p个基数b位数可以用n舍入为浮点数 十进制数字,然后再次返回而不更改值,
{ p log10 b if b is a power of 10 { { [^1 + p log10 b^] otherwise
FLT_DECIMAL_DIG 6
DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10
“超自然”是指:
What is a subnormal floating point number?
当指数位为零且尾数为0时,数字是次正规的 不为零。它们是介于零和最小法线之间的数字 数。他们在尾数中没有隐含的前导1。
强烈建议:
如果您不熟悉“浮点算术”(或者坦率地说,即使您不熟悉),这也是阅读(或阅读)的出色文章:
What Every Programmer Should Know About Floating-Point Arithmetic