C++标准说:
如果在表达式的计算过程中,结果没有数学定义或不在其类型的可表示值范围内,则行为未定义。
和
二元/运算符将第一个表达式除以第二个表达式得到商。如果商a/b在结果的类型中是可表示的[snip];否则,a/b的行为未定义。
如果我有:
double x = 1.0 / 3.0;
1除以3的结果是实数“三分之一”。三分之一不能在结果类型中表示(double
,通常是IEEE754双精度binary64浮点类型)。
这是否意味着上面的语句是一个未定义的操作?当然不可能--但我不明白为什么不可能?
4条答案
按热度按时间bjg7j2ky1#
词语“范围”在此是指在最低和最高可表示值之间。它不是数学术语“域”和“范围”。
真实的值可能会超出浮点类型的范围,因为它太大(远离零)或太小(非零值太接近零)。
浮点类型不精确;这是他们的天性。一些计算产生的值是精确真实的值的近似值。如果此类计算产生垃圾结果或异常,则浮点将无法满足其目的。
kzipqqlq2#
我想你把“不能被代表”误解为“不能被完美地代表”了。
至少在典型的硬件上,如果我做了如下操作:
结果值(1 e600)超出了可以表示的范围(根本),因此结果未定义。
像
1.0 / 3.0
这样的东西不能 * 精确地 * 表示,但可以表示为某种指定的精度级别。当然,如果有人真的想,他们可以构建可以完美表示1.0/3.0
的base-3浮点硬件(当然,失去了完美表示1.0/2.0
的能力)。从稍微不同的方向来看,它在很大程度上归结为底层硬件。通常,您使用IEEE 754(或其某些变体)。该标准定义了二进制硬件上类似
1.0/3.0
的“正确”结果(到位级别)。尽管这不是三分之一,但它被定义为“正确”的结果。因此,只要你得到的结果是IEEE 754指定的,它就代表了正确的结果,即使这与你在实数数学领域所期望的不同。lvjbypge3#
这是否意味着上面的语句是一个未定义的操作?
1.0/3.0
就可以了。当像
/
这样的数学运算符不能返回 * 精确 * 数学结果作为浮点值时,返回的值是两个最接近的可表示浮点值之一,一个大一个小。即 * 舍入 *,选择取决于舍入模式。当然,对于 domain 问题,比如
x/0.0
,甚至数学结果都没有定义。如果不存在两个可表示的浮点值来约束数学结果,则会出现问题-导致UB。
通常对于 infinity,总是有2个边界值。
ftf50wuq4#
事实证明,这种解释是不正确的,并且是基于对引用序列全文的误读:
[expr. mul] p4:
对于整数操作数,/运算符产生代数商,其中任何小数部分被丢弃;如果商a/b在结果的类型中是可表示的,则(a/b)* b + a % b等于a;否则,a/b和a % b的行为都未定义。
https://eel.is/c++draft/expr.mul#4
整个引用的序列仅适用于 * 整数 * 操作数。特别地,术语“在类型中可表示”仅用于整数操作数而不是浮点操作数的上下文中。
浮点类型在标准中没有定义“可表示值的范围”,标准中也没有使用这样的概念。