【问题标题】:Book says c Standard provides floating point accuracy to six significant figures, but this isnt true?书上说 c 标准提供了六位有效数字的浮点精度,但这不是真的吗?
【发布时间】:2019-01-31 22:41:14
【问题描述】:

我正在阅读 Stephen Prata 的 C Primer Plus,它引入浮点数的第一个方法是谈论它们如何精确到某个点。它特别指出“C 标准规定浮点数必须能够表示至少六个有效数字......浮点数必须准确地表示前六个数字,例如 33.333333”

这对我来说很奇怪,因为它听起来像是浮点数精确到六位数,但事实并非如此。 1.4 存储为 1.39999... 等等。你仍然有错误。

那么究竟提供了什么?是否有一个数字应该有多准确的截止值?

在 C 中,您不能在浮点数中存储超过六个有效数字而不会收到编译器警告,但为什么呢?如果你要超过六位数,它似乎也一样准确。

关于下溢和次正规数的部分使这更加令人困惑。当您有一个浮点数可以是最小的数字并将其除以 10 时,您得到的错误似乎并不正常?它们似乎只是上面提到的常规舍入误差。

那么为什么书上说浮点数精确到六位数,而次正态与常规舍入误差有何不同?

【问题讨论】:

  • C 标准的什么地方是这么说的? port70.net/~nsz/c/c11/n1570.html
  • 例子有8位有效数字
  • 33.333333 不是我写的。直接出书了。它暗示 33.3333 将被保存,其余部分将被截断。
  • @GovindParmar: C 2018 5.2.4.2.2 12 表示FLT_DIG 必须至少为 6,它是小数位数,q,这样任何带有 q 十进制数字的浮点数(例如输入中的“1.40000e0”)都可以四舍五入为带有 p 基数 b 个数字(它指的是内部格式之一,例如 floatdoublelong double),然后再次返回而不更改 q 十进制数字。
  • @Akimbo:你在问一个关于一个非常大的主题的重要问题。知识渊博的人(尤其是 Eric Postpischil)已经给了你一些非常详细的答案。问:它帮助你吗?如果没有,请阅读thisthis 和/或this。请发回任何具体问题。

标签: c floating-point


【解决方案1】:

假设你有一个带q位有效数字的十进制数字:

dq-1.dq-2 dq-3d0,

让我们也让它成为一个浮点十进制数字,这意味着我们用十的幂来缩放它:

dq-1.dq-2 dq-3d0•10e.

接下来,我们将此数字转换为float。许多这样的数字不能在float 中精确表示,因此我们将结果四舍五入到最接近的可表示值。 (如果出现平局,我们四舍五入以使低位相等。)结果(如果我们没有上溢或下溢)是某个浮点数 x。根据浮点数的定义(在 C 2018 5.2.4.2.2 3 中),它由某个基数中的一些数字表示,该基数按该基数的幂进行缩放。假设它是底数 2,x 是:

bp-1.bp-2 bp-3b0•2p.

接下来,我们将这个float x 转换回具有q 位有效数字的十进制。类似地,floatx 可能无法准确表示为具有 q 个数字的十进制数字,因此我们得到一些可能的新数字:

nq-1.nq-2 nq-3n0•10m.

事实证明,对于任何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 在往返过程中幸存下来。

换句话说,一般来说,float 中可以不加变化地表示六个十进制数字是不正确,但确实float 携带足够的信息 strong> 你可以从中恢复六个十进制数字。

当然,一旦你开始做算术,错误通常会复合,你不能再依赖六位小数。

【讨论】:

  • 这听起来很初级,因为它是,但是当我们说我们“将其转换为十进制”时,我们究竟是什么意思?当我在 IDE 中编写一个浮点文字时,它是一个浮点文字,我将它存储在一个浮点中。 1.4 将始终为 1.39999997615814208984375。那么什么时候四舍五入到十进制发生。在一个 c 编译器中?或者当被问到时,所有计算机和所有程序都会将 1.39999997615814208984375 舍入到十进制的 1.4 位?由于整数不能是 1.4,并且使用浮点数来表示它们,我认为十进制形式更像是一种想法,而不是我们转换成的实际事物(至少,转换成计算机)。
  • @Akimbo:一般来说,转换是一种操作(或函数),它的输入是一种类型,输出是另一种类型,并且输出值尽可能接近输入值。例如,将指向int 的指针转换为指向char 的指针会生成指向内存中相同位置但类型不同的指针。将float 中的三个转换为int 会在int 中产生三个。这只是表示形式的变化,价值的变化尽可能小。
  • @Akimbo:当1.4f出现在程序的源文本中时,在翻译(编译)过程中将其转换为float。 C 实现(通常是此时的编译器)对其进行舍入(通常使用舍入到最近的关系到偶数,但其他规则也是可能的)。如果你写float x = 1.4;,那么1.4被转换为double,因为没有f1.4被解释为double常量,那么,因为它被用来初始化一个float,它被转换为float。当您使用printf%f%g 等格式打印它时,它会转换为十进制。
  • 那么就C而言,由于十进制并不是真正的数据类型,而是一种表示输出的方式,这是否意味着这仅与IO函数相关?如果幕后发生的一切都是浮点算术,并且对于像 printf 这样的函数,它只真正转换为十进制,那么这本书部分是否是一种冗长的说法:“像 printf 这样转换为十进制的函数被精确地四舍五入到六个 sig 数字?”编辑:我确实了解铸造和浮动文字,我只是对这六位数的实际含义在哪里出现而不是 printf 感到困惑
  • @Akimbo:它告诉你的是一种衡量float 中有多少信息的量度。这意味着,如果您将一个十进制数字(以任何方式)转换为 float,然后再将其转换回来(以任何方式),那么您将得到原始数字,前提是它最多具有 q 位,然后将结果四舍五入为 q 位。 (转换必须使用正确的舍入完成;有些软件对此很草率。)转换可以由scanfprintf 完成,或者通过从源文本编译或由您自己的软件完成。这只是说floatq 个数字就足够了。
【解决方案2】:

感谢 Govind Parmar 引用了 C11(或就此而言 C99)的在线示例。

您所指的“6”是“FLT_DECIMAL_DIG”。

http://c0x.coding-guidelines.com/5.2.4.2.2.html

小数位数,n,使得任何带有 p radix 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?

当指数位为零且尾数为零时,数字是次正规的 非零。它们是介于零和最小法线之间的数字 数字。它们在尾数中没有隐含的前导 1。


强烈建议:

如果您不熟悉“浮点运算”(或者,坦率地说,即使您不熟悉),这是一篇非常值得阅读(或回顾)的文章:

What Every Programmer Should Know About Floating-Point Arithmetic

【讨论】:

  • 报价中的“又回来”是什么意思?您如何对数字进行四舍五入?
  • 这是错误的方向。 FLT_DECIMAL_DIG 用于将 C 程序中的浮点对象四舍五入为十进制数字,然后返回原始浮点类型。该问题询问有关保留十进制数字的问题,这意味着您从十进制数字变为 C 对象,然后再返回十进制数字。这在标准的下一项中由FLT_DIG 涵盖。
  • 我认为这里的四舍五入是指通过转换为二进制而丢失数字。 您从字符串表示中获取p 数字,将其转换为浮点数,然后再转换回字符串表示。
  • 我不知道你知道或不知道多少,所以这是一个很难回答的问题。一些提示:1)Steven Prata 的书没有“错误”,2)C 使用 IEEE-754,并且 IEEE-754 是严格定义的。包括“例外”。 3)这是一个很好的“开始”链接:What Every Computer Scientist Should Know About Floating-Point Arithmetic
  • @paulsm4:C 标准没有说实现使用 IEEE 754(或等效的 IEC 60559)。 C 标准提供了附录 F,它指定了 IEC 60559 的使用,作为 C 实现可能采用的选项。我不知道有谁采用了它。许多 C 实现使用 IEEE-754 格式,但以各种方式未能符合它。
猜你喜欢
  • 2021-10-06
  • 1970-01-01
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
相关资源
最近更新 更多