四舍五入? ( 积分: 50 )

  • 主题发起人 主题发起人 xiaolinj79
  • 开始时间 开始时间
X

xiaolinj79

Unregistered / Unconfirmed
GUEST, unregistred user!
经典的四舍五入算法
function RoundEx(aValue: Double
aDigit: Integer): Double;
var
dTmp: Double;
begin
dTmp:=Power(10,aDigit);
if aValue < 0 then
Result := - trunc(Abs(aValue) * dTmp + 0.5) / dTmp
else
Result := trunc(aValue * dTmp + 0.5) / dTmp;
end;

大家不知道试验过没
20.455保留二位小数居然得出错误的结果
难道这个经典算法有问题?
 
跟踪进去发现
trunc(aValue * dTmp + 0.5)这里
20.455*100+0.5=2046没问题
但是trunc(2046)=2045?
发生什么事了
 
看一下这个帖子
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3840300
 
to teclick:
你能说下 上面这个函数的问题吗?
 
浮点运算单元的控制字,决定了你的结果
 
修改一下你的代码
function RoundEx(aValue: Double
aDigit: Integer): Double;
var
dTmp: Double;
RM: TFPURoundingMode;
begin
RM := GetRoundMode;
SetRoundMode(rmUp);
dTmp:=Power(10,aDigit);
if aValue < 0 then
Result := - round(Abs(aValue) * dTmp + 0.5) / dTmp
else
Result := round(aValue * dTmp + 0.5) / dTmp;
SetRoundMode(RM);
end;
 
to teclick大侠:
我试了下你修改后的DEMO
还是不对!
我又把那 ....+ 0.5....中的'+0.5'去掉了试了下
还是不对 RoundEx(20.44, 2) 是 20.45
RoundEx(20.454, 2) 是 20.46
请教SetRoundMode(rmUp);中
即TFPURoundingMode = (rmNearest, rmDown, rmUp, rmTruncate);
中各值的意思!
 
to teclick:
经过检验
修改一下你的代码
function RoundEx(aValue: Double
aDigit: Integer): Double;
var
dTmp: Double;
RM: TFPURoundingMode;
begin
RM := GetRoundMode;
SetRoundMode(rmUp);
dTmp:=Power(10,aDigit);
if aValue < 0 then
Result := - round(Abs(aValue) * dTmp + 0.5) / dTmp
else
Result := round(aValue * dTmp + 0.5) / dTmp;
SetRoundMode(RM);
end;

这段代码的结果依旧是错误的,依旧会发生偏差
个人认为是数据类型的精度引起的误差
所以我用了比较笨的解决办法
稍后放出解决代码。至少,结果上是正确的
 
应该是
SetRoundMode(rmDown);
 
to teclick:
SetRoundMode(rmDown);也不对啊!
RoundEx(20.45, 2) 是 20.44了
 
delphi群:23981160,爱好d的都来
 
我这里的结果是对的20.45 = 20.45
 
to teclick:
用了SetRoundMode(rmDown);后
RoundEx(20.45, 2) 是 20.45了
但是RoundEx(20.455, 2) 还是 20.45了
 
我是写了个大循环来测试累加的尾数四舍五入的
teclick的肯定是不符合要求的
 
完美四舍五入:
function RoundEx(aValue: Double
aDigit: Integer): Double;
begin
Result:=StrToFloat(Format('%.'+IntToStr(aDigit)'f',[aValue]));
end;
 
to liuchong
有这么写format语句的?为什么不这么写
Result := StrToFloat(Format('%.*f',[aDigit,aValue]));
而且,Format和FormatFloat同样有问题
下面是测试代码
procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
dTemp:double;
begin
dTemp := 0.255;
for i:=1 to 300 do
begin
Memo1.Lines.Add(RoundEx(i+dTemp,2));
end;
end;

function TForm1.RoundEx(aValue: Double
aDigit: Integer): string;
begin
Result:=Format('%.*f',[aDigit,aValue]);
end;

结果是
1.25
2.25
3.25
4.25
5.25
6.25
7.25
8.26
9.26
10.26
11.26
12.26
13.26
14.26
15.26
16.25
17.25
18.25
19.25
20.25
21.25
22.25
23.25
24.25
25.25
26.25
27.25
28.25
29.25
30.25
31.25
32.26
33.26
34.26
35.26
36.26
37.26
38.26
39.26
40.26
41.26
42.26
43.26
44.26
45.26
46.26
47.26
48.26
49.26
50.26
51.26
52.26
53.26
54.26
55.26
56.26
57.26
58.26
59.26
60.26
61.26
62.26
63.26
64.25
65.25
66.25
67.25
68.25
69.25
70.25
71.25
72.25
73.25
74.25
75.25
76.25
77.25
78.25
79.25
80.25
81.25
82.25
83.25
...
明显...不能用
 
不是那个函数不能用
是因为浮点数是近似值,虽然你写的是0.255,实际在内存中,
它是0.249999999999999999
这是正常的,不是函数的问题,你可以修正这个数值
使用这个函数:
function FixFloat(const D:Double):Double;
begin
Result:=StrToFloat(Format('%.f',[D]));
end;

RoundEx(FixFloat(dTemp),2));
 
(直接写的浮点数,在内存中不是那个数,都差那么0.000........1)
 
function FixFloat(const D:Double;aDigit:Byte):Double;
begin
Result:=StrToFloat(Format('%.*f',[aDigit,D]));
end;


function RoundEx(aValue: Double
aDigit:Byte):String;
begin
Result:=Format('%*f',[aDigit,aValue]);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
dTemp:double;
begin
dTemp := 0.255;
dTemp:=FixFloat(dTemp,2);
for i:=1 to 300 do
begin
Memo1.Lines.Add(RoundEx(i+dTemp,2));
end;
end;
 
时间过去蛮久了没上来看看
对不起关心问题的朋友了
问题已经解决了
解决的方式却有点没仔细推敲
年底忙,回头帖下代码大家讨论下
 
后退
顶部