这种与浮点文字零的比较有效吗?
Is this comparison to floating point literal zero valid?
来自 N3337(C++11 草案)第 3.9.1.8 节:
The value representation of floating-point types is implementation-defined.
这是否适用于浮点类型的所有用法,无论它是否为文字?这是让我有些担心的例子:
float foo{0.0f};
if (foo == 0.0f)
{
// Am I always guaranteed to get here?
}
如果我们假设 0.0f
就实现而言不是真正的 0,而是一些未定义的数字,这种比较在技术上是否仍然有效,因为两个操作数都是通过常量获得的,即使我可能不知道它的真实价值,他们还是一样吗?
像这样与 float 文字的相等比较总是有代码味道,我只是想确保在某些用例中这没有意义或有效。
是的,您一定会到达那里。对相关数字进行处理操作后出现浮点数不精确。常量在您的情况下是安全的。
但是,如果您通过提供过多的小数来超出浮点数精度,或者您使用其他数据类型初始化浮点数,则可能会得到不同的解释。
例如,这可能不会成功:
float foo{2.1234321f};
if (foo * 6.1234321f / 0.1234321f == 105.3428750f)
{
// Am I always guaranteed to get here? Not at all.
}
如果你想在比较浮点数时安全,你应该"approximate"结果。请参阅下面的代码。
#include <limits>
#include <type_traits>
using namespace std;
class exact{};
class approx{};
template<class> struct tolerance;
template<>
struct tolerance<float>
{
static constexpr float value() { return 0.00001; }
}
template<class T>
bool close_enough(T a, T b, exact)
{
return a == b;
}
template<class T>
bool close_enough(T a, T b, approx)
{
return abs(a - b) <= tolerance<T>::value();
}
template<class T>
bool close_enough(T a, T b)
{
return close_enough(a, b,
conditional<numeric_limits<T>::is_exact, exact, approx>::type{});
}
int main()
{
float a = 2.1234321f, b = 105.3428750f;
if (close_enough(a * 6.1234321f / 0.1234321f, b))
{
// Am I always guaranteed to get here? Yes!
}
else
{
// ...
}
}
据我所知是的,可以保证去那个街区。因为 0.0f 是一个浮点常数。
float f = 0.0; /* OK, throw away bits to convert 0.0 from double to float */
assert ( f == 0.0 ); /* not OK, f is converted from float to double
and the value of 0.0 depends on how many bits you use to represent it. */
assert ( f == 0.0f ); /* OK, comparing two floats, although == is finicky. */
另请注意,在 float x = 0
中存在从 int 到 float 的隐式类型转换。
在 float x = 0.0f
中我们没有这样的类型转换。在 float x = 0.0
中,我们有一个从 double 到 float 的隐式类型转换。
很好的阅读:what every computer scientist should know about floating-point arithmetic
在实践中
float foo = 0.0 ;
if (foo == 0.0)
{
}
倾向于工作。然而,做
float foot = SOMEARBITRARYCONSTANT ;
if (foo == SOMEARBITRARYCONSTANT)
{
}
可能无效。
当 SOMEARBITRARYCONSTANT 是一个可以精确表示的值时,它往往会起作用,否则可能会失败。
我见过相当于:
float foo = 0.1 ;
if (foo == 0.1)
{
}
无效,因为编译器以不同方式舍入 0.1。
一般来说,浮点数 == 是个坏主意。
虽然标准中关于浮点数的技术保证很少,但 CPU 仅使用两种不同的表示形式:二进制编码的十进制数和 IEEE 754。现在第一个相当深奥,默认情况下从未使用过,因为它提供的精度低于 IEEE 浮点数,因此可以安全地假设只要你有浮点数,你就有 IEEE 数字。
为什么这很重要?仅仅是因为 IEEE 格式保证了很多值的精度。具体来说,它定义了 +0.0
和 -0.0
是什么,并且从格式规范可以看出 float
可以表示 -16777216
到 16777216
范围内的所有整数确切地。 (同样,double
格式允许 -2^53
到 2^53
的范围,这很舒服。平等比较。
但是,无论何时您使用这些信息,您还应该对您的推论进行评论,您不能假设其他人会发现它们是显而易见的...
来自 N3337(C++11 草案)第 3.9.1.8 节:
The value representation of floating-point types is implementation-defined.
这是否适用于浮点类型的所有用法,无论它是否为文字?这是让我有些担心的例子:
float foo{0.0f};
if (foo == 0.0f)
{
// Am I always guaranteed to get here?
}
如果我们假设 0.0f
就实现而言不是真正的 0,而是一些未定义的数字,这种比较在技术上是否仍然有效,因为两个操作数都是通过常量获得的,即使我可能不知道它的真实价值,他们还是一样吗?
像这样与 float 文字的相等比较总是有代码味道,我只是想确保在某些用例中这没有意义或有效。
是的,您一定会到达那里。对相关数字进行处理操作后出现浮点数不精确。常量在您的情况下是安全的。
但是,如果您通过提供过多的小数来超出浮点数精度,或者您使用其他数据类型初始化浮点数,则可能会得到不同的解释。
例如,这可能不会成功:
float foo{2.1234321f};
if (foo * 6.1234321f / 0.1234321f == 105.3428750f)
{
// Am I always guaranteed to get here? Not at all.
}
如果你想在比较浮点数时安全,你应该"approximate"结果。请参阅下面的代码。
#include <limits>
#include <type_traits>
using namespace std;
class exact{};
class approx{};
template<class> struct tolerance;
template<>
struct tolerance<float>
{
static constexpr float value() { return 0.00001; }
}
template<class T>
bool close_enough(T a, T b, exact)
{
return a == b;
}
template<class T>
bool close_enough(T a, T b, approx)
{
return abs(a - b) <= tolerance<T>::value();
}
template<class T>
bool close_enough(T a, T b)
{
return close_enough(a, b,
conditional<numeric_limits<T>::is_exact, exact, approx>::type{});
}
int main()
{
float a = 2.1234321f, b = 105.3428750f;
if (close_enough(a * 6.1234321f / 0.1234321f, b))
{
// Am I always guaranteed to get here? Yes!
}
else
{
// ...
}
}
据我所知是的,可以保证去那个街区。因为 0.0f 是一个浮点常数。
float f = 0.0; /* OK, throw away bits to convert 0.0 from double to float */
assert ( f == 0.0 ); /* not OK, f is converted from float to double
and the value of 0.0 depends on how many bits you use to represent it. */
assert ( f == 0.0f ); /* OK, comparing two floats, although == is finicky. */
另请注意,在 float x = 0
中存在从 int 到 float 的隐式类型转换。
在 float x = 0.0f
中我们没有这样的类型转换。在 float x = 0.0
中,我们有一个从 double 到 float 的隐式类型转换。
很好的阅读:what every computer scientist should know about floating-point arithmetic
在实践中
float foo = 0.0 ;
if (foo == 0.0)
{
}
倾向于工作。然而,做
float foot = SOMEARBITRARYCONSTANT ;
if (foo == SOMEARBITRARYCONSTANT)
{
}
可能无效。
当 SOMEARBITRARYCONSTANT 是一个可以精确表示的值时,它往往会起作用,否则可能会失败。
我见过相当于:
float foo = 0.1 ;
if (foo == 0.1)
{
}
无效,因为编译器以不同方式舍入 0.1。
一般来说,浮点数 == 是个坏主意。
虽然标准中关于浮点数的技术保证很少,但 CPU 仅使用两种不同的表示形式:二进制编码的十进制数和 IEEE 754。现在第一个相当深奥,默认情况下从未使用过,因为它提供的精度低于 IEEE 浮点数,因此可以安全地假设只要你有浮点数,你就有 IEEE 数字。
为什么这很重要?仅仅是因为 IEEE 格式保证了很多值的精度。具体来说,它定义了 +0.0
和 -0.0
是什么,并且从格式规范可以看出 float
可以表示 -16777216
到 16777216
范围内的所有整数确切地。 (同样,double
格式允许 -2^53
到 2^53
的范围,这很舒服。平等比较。
但是,无论何时您使用这些信息,您还应该对您的推论进行评论,您不能假设其他人会发现它们是显而易见的...