10

正如我所解释的,MSDN 的定义几乎numeric_limits::is_exact总是错误的:

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

而且IBM的定义几乎总是正确的:(或者循环定义,取决于你怎么读)

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

我可以肯定的是,我可以将 a 存储2在 adouble和 a中long,并且它们都将被精确地表示。

然后我可以将它们除以并且两者都不能准确地10保持数学结果。

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

编辑:我已经从许多答案中提供的详细信息中发布了我认为对该问题 的准确答案。这个答案不是赏金的竞争者

4

6 回答 6

7

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

另一方面,关于浮点类型应该表示的内容,C++ 没有实际意义。据我所知,一个实现可以通过floatint、的double同义词long和.long doublelong long

再次来自标准is_exact

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

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

  • 以某种特殊的方式定义floatdouble和,或long double
  • 提供一些非标准的有理或定点类型作为算术类型,并决定std::numeric_limits<non_standard_type>为这些非标准扩展提供一个。
于 2013-01-07T22:41:38.790 回答
5

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

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

于 2013-01-08T15:55:44.073 回答
3

精确的问题不限于 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 分开的微不足道的情况),显然存在这样的对,所以我们可以说浮点(操作)是不精确的。

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

对于带符号的 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 的欧几里得除法的商)。

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

于 2013-01-10T23:37:55.717 回答
2

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

static constexpr bool is_exact;

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

对所有专业都有意义。

于 2013-01-07T20:40:28.927 回答
2

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

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

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

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

于 2013-01-11T02:36:38.073 回答
2

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

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

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

这意味着问题中做出的两个假设——虽然对于普遍存在的IEEE 浮点标准是正确的——但对于 C++ 定义是不正确的:

我确信我可以将 a2完全存储在 double 中。

然后我可以将 [it] 除以 [ 10double不会]准确地保持数学结果。

于 2013-01-12T20:18:10.857 回答