为什么1.0 / 3.0不是C++中未定义的操作?(不能以浮动表示)

oprakyz7  于 2023-06-25  发布在  其他
关注(0)|答案(4)|浏览(167)

C++标准说:
如果在表达式的计算过程中,结果没有数学定义或不在其类型的可表示值范围内,则行为未定义。

二元/运算符将第一个表达式除以第二个表达式得到商。如果商a/b在结果的类型中是可表示的[snip];否则,a/b的行为未定义。
如果我有:

  1. double x = 1.0 / 3.0;

1除以3的结果是实数“三分之一”。三分之一不能在结果类型中表示(double,通常是IEEE754双精度binary64浮点类型)。
这是否意味着上面的语句是一个未定义的操作?当然不可能--但我不明白为什么不可能?

bjg7j2ky

bjg7j2ky1#

词语“范围”在此是指在最低和最高可表示值之间。它不是数学术语“域”和“范围”。
真实的值可能会超出浮点类型的范围,因为它太大(远离零)或太小(非零值太接近零)。
浮点类型不精确;这是他们的天性。一些计算产生的值是精确真实的值的近似值。如果此类计算产生垃圾结果或异常,则浮点将无法满足其目的。

kzipqqlq

kzipqqlq2#

我想你把“不能被代表”误解为“不能被完美地代表”了。
至少在典型的硬件上,如果我做了如下操作:

  1. double x = 1e300 / 1e-300;

结果值(1 e600)超出了可以表示的范围(根本),因此结果未定义。
1.0 / 3.0这样的东西不能 * 精确地 * 表示,但可以表示为某种指定的精度级别。当然,如果有人真的想,他们可以构建可以完美表示1.0/3.0的base-3浮点硬件(当然,失去了完美表示1.0/2.0的能力)。
从稍微不同的方向来看,它在很大程度上归结为底层硬件。通常,您使用IEEE 754(或其某些变体)。该标准定义了二进制硬件上类似1.0/3.0的“正确”结果(到位级别)。尽管这不是三分之一,但它被定义为“正确”的结果。因此,只要你得到的结果是IEEE 754指定的,它就代表了正确的结果,即使这与你在实数数学领域所期望的不同。

lvjbypge

lvjbypge3#

这是否意味着上面的语句是一个未定义的操作?
1.0/3.0就可以了。
当像/这样的数学运算符不能返回 * 精确 * 数学结果作为浮点值时,返回的值是两个最接近的可表示浮点值之一,一个大一个小。即 * 舍入 *,选择取决于舍入模式。
当然,对于 domain 问题,比如x/0.0,甚至数学结果都没有定义。
如果不存在两个可表示的浮点值来约束数学结果,则会出现问题-导致UB。
通常对于 infinity,总是有2个边界值。

ftf50wuq

ftf50wuq4#

事实证明,这种解释是不正确的,并且是基于对引用序列全文的误读:
[expr. mul] p4:
对于整数操作数,/运算符产生代数商,其中任何小数部分被丢弃;如果商a/b在结果的类型中是可表示的,则(a/b)* b + a % b等于a;否则,a/b和a % b的行为都未定义。
https://eel.is/c++draft/expr.mul#4
整个引用的序列仅适用于 * 整数 * 操作数。特别地,术语“在类型中可表示”仅用于整数操作数而不是浮点操作数的上下文中。
浮点类型在标准中没有定义“可表示值的范围”,标准中也没有使用这样的概念。

相关问题