- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我在不同的构建/执行场景下得到不同的浮点舍入。注意下面第二次运行中的 2498
...
#include <iostream>
#include <cassert>
#include <typeinfo>
using std::cerr;
void domath( int n, double c, double & q1, double & q2 )
{
q1=n*c;
q2=int(n*c);
}
int main()
{
int n=2550;
double c=0.98, q1, q2;
domath( n, c, q1, q2 );
cerr<<"sizeof(int)="<<sizeof(int)<<", sizeof(double)="<<sizeof(double)<<", sizeof(n*c)="<<sizeof(n*c)<<"\n";
cerr<<"n="<<n<<", int(q1)="<<int(q1)<<", int(q2)="<<int(q2)<<"\n";
assert( typeid(q1) == typeid(n*c) );
}
作为 64 位可执行文件运行...
$ g++ -m64 -Wall rounding_test.cpp -o rounding_test && ./rounding_test
sizeof(int)=4, sizeof(double)=8, sizeof(n*c)=8
n=2550, int(q1)=2499, int(q2)=2499
作为 32 位可执行文件运行...
$ g++ -m32 -Wall rounding_test.cpp -o rounding_test && ./rounding_test
sizeof(int)=4, sizeof(double)=8, sizeof(n*c)=8
n=2550, int(q1)=2499, int(q2)=2498
在 valgrind 下作为 32 位可执行文件运行...
$ g++ -m32 -Wall rounding_test.cpp -o rounding_test && valgrind --quiet ./rounding_test
sizeof(int)=4, sizeof(double)=8, sizeof(n*c)=8
n=2550, int(q1)=2499, int(q2)=2499
为什么我在使用 -m32
编译时看到不同的结果,为什么在运行 valgrind 时结果又不同?
我的系统是Ubuntu 14.04.1 LTS x86_64,我的gcc是4.8.2版本。
编辑:
为了响应反汇编的要求,我对代码进行了一些重构,以便将相关部分隔离开来。 -m64
和 -m32
之间采用的方法显然有很大不同。我不太关心为什么这些会给出不同的舍入结果,因为我可以通过应用 round()
函数来解决这个问题。最有趣的问题是:为什么 valgrind 会改变结果?
rounding_test: file format elf64-x86-64
<
000000000040090d <_Z6domathidRdS_>: <
40090d: 55 push %rbp <
40090e: 48 89 e5 mov %rsp,%rbp <
400911: 89 7d fc mov %edi,-0x4(%rbp <
400914: f2 0f 11 45 f0 movsd %xmm0,-0x10(%r <
400919: 48 89 75 e8 mov %rsi,-0x18(%rb <
40091d: 48 89 55 e0 mov %rdx,-0x20(%rb <
400921: f2 0f 2a 45 fc cvtsi2sdl -0x4(%rbp), <
400926: f2 0f 59 45 f0 mulsd -0x10(%rbp),%x <
40092b: 48 8b 45 e8 mov -0x18(%rbp),%r <
40092f: f2 0f 11 00 movsd %xmm0,(%rax) <
400933: f2 0f 2a 45 fc cvtsi2sdl -0x4(%rbp), <
400938: f2 0f 59 45 f0 mulsd -0x10(%rbp),%x <
40093d: f2 0f 2c c0 cvttsd2si %xmm0,%eax <
400941: f2 0f 2a c0 cvtsi2sd %eax,%xmm0 <
400945: 48 8b 45 e0 mov -0x20(%rbp),%r <
400949: f2 0f 11 00 movsd %xmm0,(%rax) <
40094d: 5d pop %rbp <
40094e: c3 retq <
| rounding_test: file format elf32-i386
> 0804871d <_Z6domathidRdS_>:
> 804871d: 55 push %ebp
> 804871e: 89 e5 mov %esp,%ebp
> 8048720: 83 ec 10 sub $0x10,%esp
> 8048723: 8b 45 0c mov 0xc(%ebp),%eax
> 8048726: 89 45 f8 mov %eax,-0x8(%ebp
> 8048729: 8b 45 10 mov 0x10(%ebp),%ea
> 804872c: 89 45 fc mov %eax,-0x4(%ebp
> 804872f: db 45 08 fildl 0x8(%ebp)
> 8048732: dc 4d f8 fmull -0x8(%ebp)
> 8048735: 8b 45 14 mov 0x14(%ebp),%ea
> 8048738: dd 18 fstpl (%eax)
> 804873a: db 45 08 fildl 0x8(%ebp)
> 804873d: dc 4d f8 fmull -0x8(%ebp)
> 8048740: d9 7d f6 fnstcw -0xa(%ebp)
> 8048743: 0f b7 45 f6 movzwl -0xa(%ebp),%ea
> 8048747: b4 0c mov $0xc,%ah
> 8048749: 66 89 45 f4 mov %ax,-0xc(%ebp)
> 804874d: d9 6d f4 fldcw -0xc(%ebp)
> 8048750: db 5d f0 fistpl -0x10(%ebp)
> 8048753: d9 6d f6 fldcw -0xa(%ebp)
> 8048756: 8b 45 f0 mov -0x10(%ebp),%e
> 8048759: 89 45 f0 mov %eax,-0x10(%eb
> 804875c: db 45 f0 fildl -0x10(%ebp)
> 804875f: 8b 45 18 mov 0x18(%ebp),%ea
> 8048762: dd 18 fstpl (%eax)
> 8048764: c9 leave
> 8048765: c3 ret
最佳答案
编辑: 看起来,至少在很久以前,valgrind 的浮点计算不如“真实”计算准确。换句话说,这可以解释为什么你会得到不同的结果。参见 this在 valgrind 邮件列表上问答。
Edit2:当前的 valgrind.org 文档在其“核心限制”部分有它 here - 所以我希望它确实“仍然有效”。换句话说,valgrind 的文档说预计 valgrind 和 x87 FPU 计算之间存在差异。 “你被警告了!” (正如我们所见,使用 sse 指令执行相同的数学运算会产生与 valgrind 相同的结果,确认这是“从 80 位舍入到 64 位”的差异)
浮点计算将根据计算的具体执行方式略有不同。我不确定您想得到什么答案,所以这里有一个长篇大论的“某种答案”。
Valgrind 确实以各种方式改变了程序的确切行为(它模拟某些指令,而不是实际执行真正的指令——这可能包括保存计算的中间结果)。此外,众所周知,浮点计算“不精确”——计算结果是否精确只是运气/运气不佳的问题。 0.98 是许多无法用浮点格式精确描述的数字之一 [至少不是常见的 IEEE 格式]。
通过添加:
cerr<<"c="<<std::setprecision(30)<<c <<"\n";
我们看到输出是 c=0.979999999999999982236431605997
(是的,实际值是 0.979999...99982 或类似的数字,剩余的数字只是剩余值,因为它不是“偶数”二进制数,总会有剩余的。
这是由 gcc 生成的代码的 n = 2550;
、c = 0.98
和 q = n * c
部分:
movl $2550, -28(%ebp) ; n
fldl .LC0
fstpl -40(%ebp) ; c
fildl -28(%ebp)
fmull -40(%ebp)
fstpl -48(%ebp) ; q - note that this is stored as a rouned 64-bit value.
这是代码的 int(q)
和 int(n*c)
部分:
fildl -28(%ebp) ; n
fmull -40(%ebp) ; c
fnstcw -58(%ebp) ; Save control word
movzwl -58(%ebp), %eax
movb $12, %ah
movw %ax, -60(%ebp) ; Save float control word.
fldcw -60(%ebp)
fistpl -64(%ebp) ; Store as integer (directly from 80-bit result)
fldcw -58(%ebp) ; restore float control word.
movl -64(%ebp), %ebx ; result of int(n * c)
fldl -48(%ebp) ; q
fldcw -60(%ebp) ; Load float control word as saved above.
fistpl -64(%ebp) ; Store as integer.
fldcw -58(%ebp) ; Restore control word.
movl -64(%ebp), %esi ; result of int(q)
现在,如果在这些计算之一的中间从内部 80 位精度存储(并因此舍入)中间结果,则结果可能与计算时未保存中间值的结果略有不同。
我从 g++ 4.9.2 和 clang++ -mno-sse 得到相同的结果 - 但如果我在 clang 情况下启用 sse,它会给出与 64 位构建相同的结果。使用 gcc -msse2 -m32
到处都能得到 2499 的答案。这表明错误是由某种方式“存储中间结果”引起的。
同样,在 gcc 中优化为 -O1 将在所有地方给出 2499 - 但这是巧合,而不是某些“聪明的想法”的结果。如果您想要正确舍入计算的整数值,您最好自己舍入,因为迟早 int(someDoubleValue)
会出现“一个短”。
Edit3: 最后,使用 g++ -mno-sse -m64
也会产生相同的 2498
答案,其中使用 valgrind
在同一二进制文件上生成 2499
答案。
关于c++ - gcc '-m32' 选项在不运行 valgrind 时更改浮点舍入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31973139/
我在 ASP.NET MVC 中有一个发票页面。 我正在计算 GST。 在 javascript 中这是结果。 165.45 * 0.1 = 16.544999999999998 在 C# 中我得到
在 JavaScript 中,我想将其减少到小数点后 5 位。不过,我不能简单地四舍五入并得到 0.03085,我需要进一步查看数字并将所有数字向上舍入,以便得到 0.03086。 输入:0.0308
有人可以解释为什么 R 这样做吗?在整数值上舍入最大值和最小值似乎非常有缺陷。 summary(1:1283932) Min. 1st Qu. Median Mean 3rd Qu.
所以基本上我正在做一个物理实验,在我的表格中,我希望我的数据四舍五入到与误差相同的精度,四舍五入为 1 sig fig。 例如,如果我有以下内容: angle signif(c(1.111,2.22
考虑以下 C# 代码... double x = Math.Round(72.6d, 2, MidpointRounding.ToZero); double y = Math.Round(82.6d,
我正在学习 BigDecimal,我希望它检索我输入的确切数字,以下代码正在处理该数字,我不知道为什么 public static BigDecimal parseFromNumberString(S
double y1 = 0; double y2 = 0; double i = 0.025; double n = 2; double h1 = 2000; double h2 = 4000
所以在下面的一组代码中,出于某种原因我得到了完全错误的答案...... import java.util.*; import java.io.*; import java.lang.*; import
在 Python 中,我想将两个数字相除,如果答案不是整数,我希望将数字四舍五入为上面的数字。 例如 100/30 不是给 33.3 而是给 4。谁能建议如何做到这一点?谢谢。 最佳答案 您可以使用
我需要对一个 float 进行四舍五入。例如 4.00011 。内置函数 round() 总是在数字 > .5 时向上舍入,在 = 0 val *= 10 ** precision r
我的代码: // Convert SATOSHIS to BITCOIN static double SATOSHI2BTC(const uint64_t& value) {
我想让我的 tableView 看起来像这样: 我有问题。只有在我点击单元格后,我的右角才会变圆。当 View 出现时,它看起来像这样: 点击后像这样: 这是我的代码: extension UITab
这个问题在这里已经有了答案: Precision String Format Specifier In Swift (31 个答案) 关闭 8 年前。 除了覆盖当前转换为字符串的方法之外,是否有一种
>>> a = 0.3135 >>> print("%.3f" % a) 0.314 >>> a = 0.3125 >>> print("%.3f" % a) 0.312 >>> 我期待 0.313
我有自动将输入字段加在一起的 javascript 函数,但是添加像 1.35 + 1.35 + 1.35 这样的数字会得到 4.050000000000001 的输出,这只是一个例子。如何将总数四舍
这可能是 x86 FPU 专家的问题: 我正在尝试编写一个生成范围 [min,max] 内的随机浮点值的函数。问题是我的生成器算法(浮点 Mersenne Twister,如果你好奇的话)只返回 [1
我一定错过了一些明显的东西。 select CEILING(85/30) = 2 85/30 = 2.83333 我希望该值为 3。 CEILING 函数不应该为我取整吗? 最佳答案 尝试 SELEC
我有一个关于 eclipse rcp 中的 ctabfolders 的问题。我创建了一个 e4 RCP 应用程序,其中包含一个包含堆栈部分容器的窗口,其中包含一个堆栈。该堆栈包含 1 个部分。在这一部
Closed. This question needs details or clarity。它当前不接受答案。
我读过其他一些帖子,它们似乎对其他人有用,但当我尝试它们时,它们不起作用。我刚刚开始学习Java编程,我似乎不明白如何四舍五入。 我试过了 answer = input * input; answer
我是一名优秀的程序员,十分优秀!