我正在使用Delphi XE2 Update 3.即使是最简单的浮点数(如 3.7 )也存在精度问题。鉴于此代码(32位控制台应用程序):
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses System.SysUtils;
var s: Single; d: Double; x: Extended;
begin
Write('Size of Single ----- '); Writeln(SizeOf(Single));
Write('Size of Double ----- '); Writeln(SizeOf(Double));
Write('Size of Extended --- '); Writeln(SizeOf(Extended)); Writeln;
s := 3.7; d := 3.7; x := 3.7;
Write('"s" is '); Writeln(s);
Write('"d" is '); Writeln(d);
Write('"x" is '); Writeln(x); Writeln;
Writeln('Single Comparison');
Write('"s > 3.7" is '); Writeln(s > 3.7);
Write('"s = 3.7" is '); Writeln(s = 3.7);
Write('"s < 3.7" is '); Writeln(s < 3.7); Writeln;
Writeln('Double Comparison');
Write('"d > 3.7" is '); Writeln(d > 3.7);
Write('"d = 3.7" is '); Writeln(d = 3.7);
Write('"d < 3.7" is '); Writeln(d < 3.7); Writeln;
Writeln('Extended Comparison');
Write('"x > 3.7" is '); Writeln(x > 3.7);
Write('"x = 3.7" is '); Writeln(x = 3.7);
Write('"x < 3.7" is '); Writeln(x < 3.7); Readln;
end.
我得到了这个输出:
Size of Single ----- 4
Size of Double ----- 8
Size of Extended --- 10
"s" is 3.70000004768372E+0000
"d" is 3.70000000000000E+0000
"x" is 3.70000000000000E+0000
Single Comparison
"s > 3.7" is TRUE
"s = 3.7" is FALSE
"s < 3.7" is FALSE
Double Comparison
"d > 3.7" is TRUE
"d = 3.7" is FALSE
"d < 3.7" is FALSE
Extended Comparison
"x > 3.7" is FALSE
"x = 3.7" is TRUE
"x < 3.7" is FALSE
您可以看到extended
是唯一正确评估的类型。我认为使用像3.14159265358979323846
这样复杂的浮点数时精度只是一个问题,而不是3.7
这么简单。使用single
时的问题是有道理的。但为什么double
不起作用?
答案 0 :(得分:8)
简短回答:0.7
无法准确表示(二进制浮点值始终为分数,分母为2的幂);您存储它的数据类型的精度(以及编译器为您要比较的常量类型选择的精度)会影响该数字的表示形式并对比较产生影响。
道德:永远不要直接比较两个浮点值的相等性,除非它们是完全相同的数据类型并分配了相同的确切值。
强制性链接:What Every Computer Scientist Should Know About Floating-Point Arithmetic
另一个可能有用的链接是Delphi的Math.SameValue函数,它允许您根据特定的允许增量(差异)比较两个浮点值的近似相等性。
答案 1 :(得分:8)
必读:What Every Computer Scientist Should Know About Floating-Point Arithmetic,David Goldberg。
这个问题不是精确问题。相反,问题是可表示性问题。首先,让我们重新说明浮点数用于表示实数。有无穷无尽的实数。当然,整数也是如此。但是这里的区别在于,在特定范围内,存在有限数量的整数但是具有无限数量的实数。实际上和originally shown by Cantor一样,任何有限的实数区间都包含无数的实数值。
很明显,我们不能代表有限机器上的所有实数。那么,我们可以代表哪些数字?那么,这取决于数据类型。 Delphi浮点数据类型使用二进制表示。单(32位)和双(64位)类型符合IEEE-754标准。扩展(80位)类型是Intel特定类型。在二进制浮点中,可表示的数字具有k2 n 的形式,其中k和n是整数。请注意,我并未声称此表单的所有数字均可表示。这是不可能的,因为有无限数量的这种数字。相反,我的观点是所有可表示的数字都是这种形式。
可表示的二进制浮点数的一些示例包括:1,0.5,0.25,0.75,1.25,0.125,0.375。您的值3.7不能表示为二进制浮点值。
这与你的代码相关的意思是它没有做你期望它做的事情。您希望与3.7的值进行比较。但相反,您将与最接近的完全可表示的值进行比较,以达到3.7。作为实现细节的问题,这个最接近的可表示的值是在扩展精度的上下文中。这就是为什么使用扩展的版本看起来像你期望的那样。但是,不要认为这意味着您的变量x
等于3.7。实际上,它等于最接近的可表示的扩展精度值为3.7。
Rob Kennedy的most useful website可以显示与特定数字最接近的可表示值。在3.7的情况下,这些是:
3.7 = + 3.70000 00000 00000 00004 33680 86899 42017 73602 98112 03479 76684 57031 25 3.7 = + 3.70000 00000 00000 17763 56839 40025 04646 77810 66894 53125 3.7 = + 3.70000 00476 83715 82031 25
这些以扩展,双重,单一的顺序呈现。换句话说,这些是变量x
,d
和s
的值。
如果您查看这些值,并将它们与最接近扩展到3.7的值进行比较,您将看到为什么程序会生成它所做的输出。这里的单精度值和双精度值都大于扩展值。这是你的程序告诉你的。
我不想就如何比较浮点值提出任何建议。这样做的最佳方式总是非常关键地取决于具体问题。没有全面的建议可以提供。