如果硬编码的浮点数可以用IEEE 754的二进制格式表示,那么它是否精确?

9
例如,0、0.5、0.15625、1、2、3等是从IEEE 754转换而来的值。它们的硬编码版本是否精确?
例如:

float a=0;
if(a==0){
    return true;
}

始终返回 true 吗?其他示例:

float a=0.5;
float b=0.25;
float c=0.125;

a * b 是否总是等于 0.125,a * b == c 是否总是成立?再举一个例子:

int a=123;
float b=0.5;

乘积 a * b 是否总是等于 61.5?或者通常情况下,整数与 IEEE 754 二进制浮点数相乘是否精确?

又或者更一般地问,如果值是硬编码并且值和结果都可以用 IEEE 754 的二进制格式表示(例如:0.5 - 0.125),那么该值是否精确?


4
不,不,不,不,还是不。 - bolov
7
是的,浮点数不是某种模糊的东西,它能够给出近似答案。浮点数问题与十进制数的表示和精度问题有关,但实际操作本身并没有任何“模糊”的地方。(仅需十分确认你正在正确进行操作,无论如何我仍会非常谨慎,因为今天可行的代码在将来可能因某人将0.25更改为0.1而无法正常工作,而他们未意识到这很重要。) - jcoder
8
为什么不把0.125表示为2^-3呢?只有当你引入不能被准确表达的数字时,才会出现麻烦。例如,5 * 0.1可能不是0.5。但是,只要你在可以准确表达的数字范围内,就应该是安全的。问题在于如何识别何时你超出了这个范围,因此最好保持在安全的范围内。 - glglgl
3
"a=123"和"b=0.25",所以“a * b总是61.5吗?”不是,因为结果是30.75。这就是你的答案。由于人为因素导致某些值在IEEE-754标准下不能被完全表示,所以你永远不应编写要求值在IEEE-754中能够被完全表示的代码。 - user3386109
2
@amuse 你可以看到,这是一个很多人对其知识危险不完整,但认为自己是专家的话题(已经有两个被删除且评分极低的答案,以及到处都是评论...)。我并不是说这里有人对或错,只是在众多相互矛盾的陈述中,只有一个是正确的...而且在整个互联网上也是如此。所以,请带着怀疑的态度看待所有内容,不要立刻相信别人写的。 - deviantfan
显示剩余11条评论
4个回答

7
浮点数本身并没有模糊性,只是一些实数不能被精确表示。
与固定宽度的十进制表示相比,假设使用三个数字。整数1可以使用1.00表示,1/10可以使用0.10表示,但1/3只能使用0.33进行近似表示。
如果我们改用二进制数字,则整数1将表示为1.00(二进制数字),1/2表示为0.10,1/4表示为0.01,但1/3仍然只能进行近似表示。
但需要记住以下几点:
  • 二进制数字与十进制数字不同。1/10可以用十进制数字精确表示为0.1,但无论使用多少二进制数字都不能精确表示。
  • 实际应用中,很难跟踪哪些数字可以被表示,哪些不能。0.5可以,但0.4不行。因此,当需要精确数字时(通常是在处理货币时),不应该使用浮点数。
  • 据一些消息来源称,一些处理器在对无法精确表示的浮点数进行浮点运算时会出现奇怪的内部情况,导致结果以一种实际上是不可预测的方式变化。

(我的观点是,实际上说浮点数本质上是模糊的是一个合理的第一近似值,因此,除非您确定您的特定应用程序可以处理它们,否则请远离它们。)

如果您想了解更多细节,可以阅读著名的计算机科学家应该知道的浮点运算知识。此外,这个网站也比较易懂:浮点数指南


5
抱歉,但您是错误的。如果所有提到的处理器都使用IEEE 754标准,并且数字在二进制表示中是有限的,那么就没有理由提供不同的结果。只要涉及四舍五入,可能会出现一些差异,但在此之前不会出现差异。 - glglgl
1
@Magisch,网络序列化与数学有什么关系?这里的一切与实时性有什么关系?听起来很像你只是不想错。我们在这里只谈论IEEE754。虽然它有一些地方可以从多个可能性中选择一个,但其中没有任何未定义或随机的东西。 - deviantfan
2
@Magisch 再问一次,fp数学与fp序列化和实时场景有什么关系?如果这一点清楚了,我们可以继续。 - deviantfan
3
@Magisch,你的评论与完全无关。这就是我为什么要问的原因。 - deviantfan
4
关于“一旦您进行数学运算,其中一个偶数临时结果具有在浮点数学中不能直接有限表示的数字,它将不准确。”好的,但是“无法表示的数字并不是每个人都在谈论的内容”。 - deviantfan
显示剩余7条评论

2
不是所有的数字都可以用二进制精确表示,但像Thomas Padron-McCarthy所说,有些数字可以。这是我向非开发人员解释时使用的方式(就像Mahmut Ali一样,我也在一个非常古老的财务软件包上工作):想象一下有一个被切成256片的巨大蛋糕。现在你可以把整个蛋糕给一个人,把一半的蛋糕分给两个人,但是一旦你决定将它分成三份,你就不能再分了——要么是85,要么是86——你无法再进一步分割。浮点数也是如此。您只能在某些表示中获得精确的数字,有些数字只能被近似地表示。

1

C++不要求二进制浮点数表示。内置整数需要具有二进制表示,通常是二进制补码,但也支持一的补码和符号和大小。但浮点数可以是十进制的。

这就引出了一个问题,C++浮点数是否可以具有不具有2为质因子的基数,例如2和10。其他基数是否被允许?我不知道,上次我尝试检查时失败了。

然而,假设基数必须是2或10,则您所有的示例都涉及2的幂,并且因此可以精确表示。

这意味着大多数问题的单个答案是“是”。例外是问题“整数乘以IEEE 754二进制浮点数[精确]”。如果结果超过可用精度,则它不能是精确的,但否则它是精确的。

有关浮点表示和属性的背景信息,请参见经典著作“计算机科学家应该知道的浮点运算”


如果一个值可以在32位或64位IEEE 754中精确表示,那并不意味着它可以用其他浮点表示精确表示。这是因为不同的32位表示和不同的64位表示使用不同数量的位来保存尾数,并具有不同的指数范围。因此,在一种方式下可以精确表示的数字,可能超出其他表示的精度或范围。
你可以使用 std::numeric_limits<T>::is_iec559 (其中Tdouble等)来检查你的实现是否声称符合IEEE 754标准。然而,在启用浮点数优化时,至少g++编译器(1)错误地声称符合IEEE 754标准,但不会根据该标准正确处理例如NaN值。实际上,is_iec559只告诉你数字的表示是否符合IEEE 754标准,而不是语义是否符合。
基本上,gcc和g++不是针对不同的语义提供不同的类型,而是通过编译器选项来适应不同的语义。并且在程序的部分单独编译时,可能无法符合C++标准。

2
没错,但问题明确关注IEEE 754表示。 - glglgl
@glglgl:我添加了一段关于那个的内容。谢谢。 - Cheers and hth. - Alf
匿名的点踩者:你是出于无知才进行点踩的。请解释一下你的想法,以便可以加以纠正。谢谢。 - Cheers and hth. - Alf
在C语言中(特别是C11),如果我有一个浮点数和一个整数的联合体,并且我已经知道sizeof(float) == sizeof(int),那么如果我写入浮点数,然后从整数读取,会发生什么?你是说我不会得到一个二进制数字吗?C11在这里明确允许类型转换。请参见此问题的被接受的答案:https://dev59.com/9mgu5IYBdhLWcg3wYF-3 - George
@George:类型游戏与此无关。所以我不确定你在问什么;这对我来说没有意义,但我猜想它可能与可能的十进制浮点表示有关。在C++中,numeric_limits<T>::radix()为您提供特定浮点类型T的基数。在C++11中,它被指定为等同于C中的FLT_RADIX,这意味着对于C++11和早期版本,给定实现中所有浮点类型的基数都是相同的,就像在C中一样。它之所以存在,是因为需要它。有一个保证的十进制类型的提案,然后必须放弃等价性。 - Cheers and hth. - Alf
显示剩余2条评论

0

原则上,这应该是可行的。如果您限制自己只使用具有有限2次幂表示的数字类。

但是这很危险:如果有人因为任何原因将您的0.5更改为0.4或将您的 .0625 更改为 .065 ,那么您的代码就会变得无效。即使有过多的注释也无济于事 - 总有人会忽略它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接