【问题标题】:std::numeric_limits::is_exact ... what is a usable definition?std::numeric_limits::is_exact ...什么是可用的定义?
【发布时间】:2012-12-21 15:17:23
【问题描述】:

据我解释,numeric_limits::is_exact 中的MSDN's definition 几乎总是错误的:

在 [this] 类型上完成的 [all] 计算没有舍入误差。

IBM's definition 几乎总是正确的:(或循环定义,取决于您如何阅读)

一种对其所有值都有精确表示的类型

我可以肯定的是,我可以将2 存储在doublelong 中,并且它们都将被准确地表示

然后我可以将它们都除以10,并且两者都不会保留数学结果精确

给定任何数字数据类型T,定义std::numeric_limits<T>::is_exact 的正确方法是什么?

编辑: 我已经从许多答案中提供的详细信息中向这个问题发布了what I think is an accurate answerThis answer is not a contender for the bounty.

【问题讨论】:

  • 2 / 10 精确存储(对于long),因为整数除法的结果是整数。
  • @GManNickG 是的,并且 2.0 / 10.0 也被精确存储(对于双精度数),因为双除法的结果是双精度数。 2/10 不是 0,除非有特殊规则。
  • 我会考虑 exact 一个数字类型,x + y != x && x + y != yx != 0 && y != 0
  • 应该是 ::is_exact_as_long_as_it_does_not_overflow_otherwise_could_trigger_undefined_behavior 但名字有点太长了
  • @aka.nice:不,unsigned 类型不会永远溢出,它们会环绕。他们的模算术是由标准规定的。

标签: c++ numeric-limits


【解决方案1】:

标准中的定义(参见 NPE 的回答)不是很准确,是吗?相反,它是循环的和模糊的。

鉴于 IEC 浮点标准有一个“不精确”数字的概念(以及计算产生不精确数字时的不精确异常),我怀疑这就是名称 is_exact 的由来。请注意,在标准类型中,is_exact 仅对 floatdoublelong double 为 false。

目的是表明类型是否完全代表了基础数学类型的所有数字。对于整数类型,基础数学类型是整数的某个有限子集。由于每个整数类型精确地表示该类型所针对的整数子集的每个成员,is_exact 对于所有整数类型都是正确的。对于浮点类型,基础数学类型是实数的某个有限范围子集。 (有限范围子集的一个示例是“0 到 1 之间的所有实数”。)甚至没有办法准确地表示实数的有限范围子集。几乎所有都是不可计算的。 IEC/IEEE 格式使事情变得更糟。使用这种格式,计算机甚至不能准确地表示有理数的有限范围子集(更不用说可计算数的有限范围子集了)。

我怀疑is_exact 一词的起源是各种浮点表示模型中“不精确”数字的长期概念。也许更好的名字是is_complete

附录
语言定义的数字类型并不是“数字”表示的全部和全部。定点表示本质上是整数,因此它们也是精确的(表示中没有漏洞)。将有理数表示为一对标准整数类型(例如,int/int)并不精确,但将有理数表示为 Bignum 对的类至少在理论上是“精确的”。

真实的呢?没有办法准确地表示实数,因为几乎所有的实数都是不可计算的。我们可以用计算机做的最好的事情就是可计算的数字。这需要将一个数字表示为某种算法。虽然这在理论上可能有用,但从实践的角度来看,它根本没用。

第二个附录
开始的地方是标准。 C++03 和 C++11 都将is_exact 定义为是

如果类型使用精确表示,则为真。

这既模糊又循环。这是没有意义的。并非完全没有意义的是整数类型(charshortintlong 等)对于法令来说是“精确的”:

所有整数类型都是精确的,...

其他算术类型呢?首先要注意的是,唯一的其他算术类型是浮点类型floatdoublelong double (3.9.1/8):

共有三种浮点类型:floatdoublelong double。 ...浮点类型的值表示是实现定义的。整数和浮点类型统称为算术类型。

C++ 中浮点类型的含义非常模糊。与 Fortran 比较:

实数数据是处理器对实数值的近似值。

与 ISO/IEC 10967-1 语言无关算术(C++ 标准在脚注中引用,但从不作为规范性参考)进行比较:

浮点类型 F 应该是 ℝ 的有限子集。

另一方面,C++ 对浮点类型应该表示的内容没有实际意义。据我所知,实现可以让float 成为int 的同义词,double 成为long 的同义词,而long double 成为long long 的同义词。

再次来自is_exact的标准:

...但并非所有精确类型都是整数。例如,有理和固定指数表示是精确的但不是整数。

这显然不适用于用户开发的扩展,原因很简单,即不允许用户定义std::whatever<MyType>。这样做,您正在调用未定义的行为。这个最后的子句只能适用于

  • 以某种特殊方式定义 floatdoublelong double,或者
  • 提供一些非标准有理数或定点类型作为算术类型,并决定为这些非标准扩展提供std::numeric_limits<non_standard_type>

【讨论】:

  • 有趣。并且可能死机。你能评论一下有理指数和固定指数是精确的吗? int/intint*2^k 是基础数学类型的合理形式,而 int*2^int 不是吗?定义中似乎有人类解释的元素,好像isnt_intimidating 本来可以恰如其分。
  • 问题是:如果你考虑浮点实数的底层类型,那么你就不懂机器浮点。当然,有理数会遇到与浮点数相同的问题,即使您认为底层类型是有理的,而不是实数。
  • @DrewDormann - 这取决于一个人如何代表理性。使用一对固定长度整数(例如,一对int 数字)的表示将遇到与floatdouble 相同的问题。另一方面,使用一对任意长度整数的表示不会遇到这些问题,至少理论上不会。 (实际上它会。内存是有限的。)
  • Re “目的是表明该类型是否准确地表示了底层数学类型的所有数字。”如果这就是意思,is_exact 将永远是正确的。根据 IEEE-754 和 ISO/IEC 10967-1,浮点值是单个数字。它不代表间隔或任何类型的模糊性。 is_exact 可以表明 operations 是否总是返回准确的结果。从表面上看,这必须始终为假,因为整数 7/3 返回的结果向下舍入为最接近的整数。所以is_exsct 只能通过更复杂的定义才有意义。
  • 我怀疑委员会没有充分考虑is_exact,也没有给它一个明确的含义。要准确地声明整数,您必须将 / 运算符定义为 trunc(x / y) 而不是数学除法 x i> / y。但是,如果你允许整数,那么浮点运算可以类似地定义:浮点x/y被定义为数学 x / y 舍入到最接近的可表示值。所以浮点 x/y 与整数 x/y 完全相同。
【解决方案2】:

我建议 is_exact 为真,如果该类型的所有文字都有其确切值。所以 is_exact 对于浮点类型是假的,因为字面量 0.1 的值不完全是 0.1。

根据 Christian Rau 的评论,当类型的任何两个值之间的四个算术运算的结果超出范围或可以使用该运算的定义精确表示时,我们可以改为将 is_exact 定义为 true类型(即截断整数除法,无符号环绕)。通过这个定义,您可以发现浮点运算是定义以产生最接近的可表示值。不要:-)

【讨论】:

  • 但这又只是我们人类和计算机之间的基础差异的结果。所以十进制浮点类型是准确的,因为我们没有以二进制格式指定浮点文字?
  • 我想我明白了。所以is_exact 没有描述数字格式本身的任何方面,它描述了 C++ 自己关于如何以该格式键入文字的决定。因为 C++ 描述了以 10 为底的双精度数,而不是例如m13e-6,双打不准确。这是你的意思吗?
  • @Christian Rau - 好吧,也许 :-) 或者,我们可以将 is_exact 定义为 true 那些类型的任何一对值之间的四个算术运算的结果超出范围或可以精确表示(前提是整数除法被定义为给出整数结果和无符号算术换行)。这可能是一个更好的定义。
【解决方案3】:

exactnes 的问题并不局限于 C,所以让我们进一步看看。

Germane 关于修订标准的讨论分开,不精确必须适用于需要四舍五入以表示具有相同类型的结果的数学运算。例如,Scheme 通过精确运算和精确字面常量来定义精确/不精确,参见 R5RS §6。来自http://www.schemers.org/Documents/Standards/R5RS/HTML的标准程序

对于double x=0.1 的情况,我们要么认为 0.1 是一个定义明确的双精度字面量,要么就像在 Scheme 中那样,该字面量是一个由不精确的编译时操作形成的不精确的常量(四舍五入到最接近的双精度操作结果1/10,在 Q 中有很好的定义)。所以我们总是以运营告终。

让我们专注于 +,其他的可以通过 + 和 group 属性在数学上定义。

不精确的可能定义是:

If there exists any pair of values (a,b) of a type such that a+b-a-b != 0,
then this type is inexact (in the sense that + operation is inexact).

对于我们知道的每一个浮点表示(nan 和 inf 分开的平凡情况)显然存在这样的对,所以我们可以看出浮点(操作)是不精确的。

对于定义明确的无符号算术模型,+ 是精确的。

对于signed int,我们在溢出的情况下存在UB问题,所以不保证准确性......除非我们改进规则以应对这种破碎的算术模型:

If there exists any pair (a,b) such that (a+b) is well defined
and a+b-a-b != 0,
then the + operation is inexact.

明确定义也可以帮助我们扩展到其他操作,但这并不是必需的。
然后,我们必须将 / 的情况视为错误多态性而不是不精确性
(/ 被定义为 int 的欧几里得除法商)。

当然,这不是官方规定,这个答案的有效性仅限于理性思考的努力

【讨论】:

    【解决方案4】:

    C++ 标准中给出的定义似乎相当明确:

    static constexpr bool is_exact;

    如果类型使用精确表示,则为真。所有整数类型都是精确的,但并非所有精确类型都是 整数。例如,有理和固定指数表示是精确的,但不是整数。

    对所有专业都有意义。

    【讨论】:

    • 但是“精确表示”是什么意思。所有浮点值都是它们所代表的值的精确表示。并且有理和固定指数表示(更不用说整数)没有 pi 的精确表示。
    • @JamesKanze - 所有浮点值都是它们所代表的值的精确表示。不,它们不是。每个浮点值代表实数线上的一个区间,而不是一个特定的数字。该间隔中的一个点将具有精确的表示。所有其他的都不会,如果启用了该异常,获取其中一个不精确值将引发 IEEE 不精确异常。
    • @JamesKanze - 我非常了解机器浮点。你的概念是看待它的一种方式,但它不是唯一的方式。将 IEEE/IEC 标准视为表示间隔本质上是 Microsoft Excel 看待事物的方式,因此它们是四舍五入的。见stackoverflow.com/questions/6930786/…
    • @Yakk 浮点类型是not R 的表示。人们也可以争论int 是否是Z 的表示,但只要没有溢出和没有分裂,他们是。除法是一个问题,因为 Z 在除法方面没有关闭:1/3 要么是 0.3333... 要么是域错误。但绝不是 0; 0 只能视为舍入误差。
    • @DavidHammen 我几乎不会将 Excel 作为参考(但我严重怀疑它是否会进行区间算术)。 IEEE 浮点值是一个精确值;对它的操作遵循精确的规则,并且也会产生一个精确的值(这不一定与它在 R 上的算术相同)。区间算术完全不同。
    【解决方案5】:

    在 C++ 中,int 类型用于表示数学整数类型(即 {..., -1, 0, 1, ...} 的集合之一)。由于实现的实际限制,该语言定义了该类型应持有的minimum range of values,并且该范围内的所有有效值必须在所有已知架构上无歧义地表示。

    该标准还定义了用于保存浮点数的类型,每个类型都有自己的有效值范围。您找不到的是有效浮点数列表。同样,由于实际限制,该标准允许这些类型的近似值。许多人试图说只有 IEEE 浮点标准可以表示的数字才是这些类型的精确值,但这不是标准的一部分。尽管在二进制计算机上实现该语言确实有一个如何表示双精度和浮点数的标准,但该语言中没有任何内容表明它必须在二进制计算机上实现。换句话说,浮点数不是由 IEEE 标准定义的,IEEE 标准只是一个可接受的实现。因此,如果有一个实现可以在定义 double 和 float 的值范围内保存任何值,而无需舍入规则或估计,您可以说 is_exact 对于该平台是正确的。

    严格来说,T 不能是判断类型是否为“is_exact”的唯一论据,但我们可以推断出其他一些论据。因为您可能正在使用具有标准硬件和任何公开可用的 C++ 编译器的二进制计算机,所以当您分配 0.1 的两倍值(在浮点类型的可接受范围内)时,这不是计算机将在计算中使用该变量。它使用 IEEE 标准定义的最接近的近似值。当然,如果您将文字与自身进行比较,您的编译器应该返回 true,因为 IEEE 标准非常明确。我们知道计算机没有无限的精度,因此我们期望值为 0.1 的计算不一定会以与文字值相同的近似表示结束。输入可怕的 epsilon 比较。

    为了实际回答您的问题,我想说,对于任何需要进行 epsilon 比较来测试近似相等性的类型,is_exact 应该返回 false。如果该类型的严格比较就足够了,它应该返回 true。

    【讨论】:

    • 当然严格的二进制比较可以对于 "inexact" 类型足够,这完全取决于上下文.同样,int需要 epsilon 比较来测试 近似相等,它来自近似相等的定义。如果我想比较两个ints 是否大约等于3 的容差,那么,我必须使用与3 的ε 进行比较。您不认为 3 小到足以冠以 epsilon 的名称这一事实并不会改变其性质。
    • 仅仅因为 is_exact 返回 false 并不意味着该类型没有确切的值。如果您的应用程序可以将其持有的值限制为精确的值或不使用这些值执行任何计算,则可以随意使用严格(按位)比较,但我认为这是对您的应用程序的优化。至于3,我认为您将应用程序的 epsilon 与类型的 epsilon 混为一谈
    • 这很有趣。因此,与 IEEE 明确定义的标准不同,C++ 定义 double 以包含允许范围内的 所有 个实数值。根据定义,即使 0.1 也是有效的双精度数。只是我当前的硬件碰巧不准确地存储了该值。
    • @Drew:实际上,no 有限大小的浮点格式(如double)可以表示任意范围内的all实数,因为所有实数的集合是无限大的:即使是从 0 到 1 的所有实数的集合也是无限大的。这是因为实数可以包括分子和分母中具有数万亿位数的有理数,以及只能用有理数近似的无理数,例如 pi 和 2 的平方根。
    • @PeterO。我认为没有人不同意这一点。 :-)
    【解决方案6】:

    std::numeric_limits<T>::is_exact 应该是 false 当且仅当 T 的定义允许可能无法存储的值。

    C++ 认为任何浮点字面量都是其类型的有效值。并且允许实现决定哪些值具有精确的存储表示。

    因此对于允许范围内的每个实数(例如2.00.2),C++ 总是 承诺该数字是有效的double 并且从不 strong> 承诺该值可以准确存储。

    这意味着问题中的两个假设——虽然对于无处不在的 IEEE floating point 标准是正确的——对于 C++ 定义是不正确的:

    我确信我可以将 2 完全存储在双精度中。

    然后我可以将 [it] 除以 10 并且 [double 将 not] 持有 数学结果完全正确

    【讨论】:

    • 或者,IBM 的定义是可行的,如果“表示”意味着“存储,而不是写入”,“值”意味着“写入,而不是存储”。
    • 听起来是个不错的答案,如果你能告诉我“C++ 认为任何浮点文字都是其类型的有效值”(我相信这成立,但话又说回来,我也肯定知道is_exact 是什么意思;))
    • @ChristianRau 我会寻找标准的宣传语......这是遗漏的声明。该标准定义了 floatdoublelong double 类型(作为“真实”)。它还定义了书面符号。然后它说very little关于存储。由于定义了类型而没有定义存储,因此没有根据存储来定义类型。您可以认为所有“实数”或可以写入的内容都是有效的。我采取了保守的选择。
    猜你喜欢
    • 2018-06-16
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    相关资源
    最近更新 更多