我有一些代码在Mac OSX和Linux(Ubuntu,Fedora,...)下表现不同。这是关于printf语句中的算术运算中的类型转换。代码用gcc / g ++编译。
以下
#include <stdio.h>
int main () {
float days = (float) (153*86400) / 86400.0;
printf ("%f\n", days);
float foo = days / 30.6;
printf ("%d\n", (int) foo);
printf ("%d\n", (int) (days / 30.6));
return 0;
}
在Linux上生成
153.000000
5
4
并在Mac OSX上
153.000000
5
5
为什么?
令我惊讶的是,这适用于Mac OSX和Linux
printf ("%d\n", (int) (((float)(153 * 86400) / 86400.0) / 30.6));
printf ("%d\n", (int) (153 / 30.6));
printf ("%.16f\n", (153 / 30.6));
为什么呢?我根本没有任何线索。 THX。
答案 0 :(得分:5)
试试这个:
#include <stdio.h>
int main () {
float days = (float) (153*86400) / 86400.0;
printf ("%f\n", days);
float foo = days / 30.6;
printf ("%d\n", (int) foo);
printf ("%d\n", (int) (days / 30.6));
printf ("%d\n", (int) (float)(days / 30.6));
return 0;
}
请注意会发生什么?双重浮动转换是罪魁祸首。请记住,float总是在varargs函数中转换为double。不过,我不确定为什么macos会有所不同。更好(或更糟)的IEEE算法实现?
答案 1 :(得分:4)
我希望答案在某种程度上与浮点变量的32位赋值相关,然后在打印前转换为double,留下比你传递完整双精度时更少的有效位 - 如第二个表达式。处理浮点运算会变得棘手。我仍然喜欢Kernighan和Pike的经典着作“编程风格的元素”的引用,他们说:
一位聪明的程序员曾经说过,“浮点数就像一堆沙子;每次移动一个,你就会失去一点沙子并捡起一点污垢。”
这是一个例子。它还说明了为什么浮点十进制算法在修订的IEEE 754标准中是一个好主意。
答案 2 :(得分:3)
days
的值是一个浮点计算, 具有无限精度 将完全生成5.0
。
如果在精度有限的情况下,得到稍微小于的结果,则转换为int会产生小于5的int。
问题这里是不正确使用浮点运算。你可能应该四舍五入。
原因您看到的差异是CPU处理浮点数的差异,或者(更有可能)编译器的优化程序在调整运行时和运行时的计算方面有所不同什么顺序。
答案 3 :(得分:3)
首先,请阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic。
因为30.6在IEEE 754浮点中不能完全,所以您获得的精确结果将不完全正确。因此,在转换为整数之前,您可能会略微低于5.0,然后整数转换为4。
您获得的确切结果取决于很多因素,例如如何计算中间结果。您的编译器可能正在生成使用x87浮点堆栈的代码,该堆栈使用80位寄存器进行中间计算。或者,您可能正在使用SSE2(Mac OS X上的默认设置),它使用分为4x32位或2x64位的128位向量寄存器。检查编译器的程序集输出以了解所使用的浮点运算类型。请参阅this page以获取可用于控制所用浮点指令类型的GCC命令行选项列表,尤其是-mfpmath
选项。
答案 4 :(得分:1)
两者都在同一个处理器上运行吗?我的猜测是它与平台的endianess有关,而不是操作系统。另请尝试使用(int) ((float) (days / 30.6))
代替(int) (days / 30.6)
。
另一个需要注意的是编译器版本是否相同。
我怀疑它与printf有关,试试这个:
#include <iostream>
int main () {
float days = (float) (153*86400) / 86400.0;
std::cout << days << std::endl;
float foo = days / 30.6;
std::cout << (int) foo << std::endl;
std::cout << (int) (days / 30.6) << std::endl;
return 0;
}
请将结果发表在评论中。
答案 5 :(得分:1)
嗯......我怀疑有一些不同,因为Linux机器是32位(我是对的?)而Mac是64位机器。在我的64位Linux机器上,我得到了第二组结果。
答案 6 :(得分:1)
我开始怀疑浮点表示问题。我不相信30.6具有精确的IEEE表示。也许你会因为四舍五入的问题而“幸运”。
另一种可能性是这一行的不同编译器处理:
float days = (float) (153*86400) / 86400.0;
作为一个人,我可以看到每天的秒数被取消,但是如果它将一个处理为整数上下文而另一个处于后续浮点状态,编译器可能会错过进行常量折叠的机会。上下文。任何标准大师都希望重视那里是否存在序列点?
答案 7 :(得分:1)
一个计算为float
,一个计算为double
,它们必须在Linux上以不同方式进行舍入。为什么这不是我不知道的MacOSX行为,特别是因为你不打算指定任何关于MacOSX计算机的东西。它是真正的Mac吗? PPC还是英特尔?
永远不要依赖浮点计算来完全按照你想要的方式出现。始终舍入到int
,绝不截断。
答案 8 :(得分:1)
这里有一些代码可以说明wat的发生情况比原始代码好一些:
#include <stdio.h>
int main(void)
{
volatile double d_30_6 = 30.6;
volatile float f_30_6 = 30.6;
printf("%.16f\n", 153 / 30.6); // compile-time
printf("%.16f\n", 153 / d_30_6); // double precision
printf("%.16f\n", 153 / f_30_6); // single precision
return 0;
}
volatile
变量迫使编译器不优化计算,无论优化级别如何......
答案 9 :(得分:0)
浮点数通过否定来证明上帝的存在,因为它们肯定是魔鬼的作品......
在漫长的一天运行宇宙之后的一天,上帝和撒旦聚在一起喝啤酒并开始回忆。 “嘿,还记得我们拉那个家伙的东西吗?”上帝说。
<是>,是的,“撒旦回答说,”那些日子,呃?大量的打击和诅咒到永恒的灭亡......“ 上帝说,“更不用说所有的预言和瘟疫以及一切”。 “是的 - 那些是他们的日子”。上帝叹了口气。 “说,我知道已经有一段时间但是,嗯,你觉得再做一次这样的事情感觉如何?”。“有趣的是你应该提到它,”撒旦说得很顺利。 “你知道我一直对技术感兴趣......”。
上帝说,“当然”,“有很多机会让人们出售他们的灵魂进行另一轮融资,嗯?”他笑了。 “那么,想到了什么?” “好吧”,撒旦说,“我把最好的守护进程放在上面。他们一直像魔鬼一样工作,想出一些新东西,我认为他们已经得到了它。这是我们的一点点叫'浮点'......“