来自N3337(C ++ 11草案)第3.9.1.8节:
浮点类型的值表示是实现定义的。
这是否适用于float类型的任何和所有用法,无论它是否是文字?以下是引起我一些担忧的例子:
float foo{0.0f};
if (foo == 0.0f)
{
// Am I always guaranteed to get here?
}
如果我们假设0.0f
在实现方面并不是真的0,而是一些未定义的数字,那么这种比较在技术上仍然有效,因为两个操作数都是通过常量获得的,即使我可能没有知道它的真正价值,它们两者仍然是一样的吗?
与像这样的浮点文字的平等比较总是有代码味道,我只是想确保某些用例没有意义或有效。
答案 0 :(得分:1)
是的,你保证能够到达那里。在对有关数字进行处理操作后发生浮动不精确。常数对你来说是安全的。
但是,如果通过提供过多的小数来超过浮点数精度,或者使用其他数据类型初始化浮点数,则可能会有不同的解释。
例如,这可能不会成功:
float foo{2.1234321f};
if (foo * 6.1234321f / 0.1234321f == 105.3428750f)
{
// Am I always guaranteed to get here? Not at all.
}
如果你想比较浮点数时要安全,你应该"近似"结果。请参阅下面的代码。
#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
{
// ...
}
}
答案 1 :(得分:0)
据我所知,肯定会去那个街区。因为 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
答案 2 :(得分:0)
虽然标准中关于浮点数的技术上几乎没有保证,但CPU只对它们使用两种不同的表示:二进制编码的十进制和IEEE 754。现在第一个是相当深奥的,并且从未默认使用,因为它提供的精度低于IEEE浮点数,因此可以安全地假设只要有浮点就有IEEE数。
为什么这很重要?仅仅因为IEEE格式保证了很多值的精度。具体来说,它定义了+0.0
和-0.0
是什么,并且从格式规范开始,float
可以表示-16777216
到16777216
范围内的所有整数究竟。 (同样地,double
格式允许范围-2^53
到2^53
,这非常舒适。)从这里,您可以扣除计算保持精确的时间,从而允许平等比较。
但是,每当您使用此信息时,您还应该在扣除时写下评论,您不能认为其他人会发现它们显而易见......
答案 3 :(得分:-1)
实践中
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。
一般来说,浮点==是一个坏主意。