任意の少数点桁で四捨五入する
|
87 |
HalfAdjust |
動作確認 |
Delphi2007 |
更新日 |
2008/02/20(水) 2010/07/22(木) |
DelphiではRoundのHELPに
少数点を四捨五入するための関数
function RoundOff(X: Extended): Longint;
があります。
これを応用して任意の桁(整数部分でも少数点以下部分でも)で
四捨五入して上の桁に揃えてしまう関数を作りました。
────────────────────
//HELPにのっているRoundOff
function RoundOff(X: Extended): Longint; overload;
begin
if x >= 0 then Result := Trunc(x + 0.5)
else Result := Trunc(x - 0.5);
end;
{-------------------------------
// RoundOff
機能: 任意の桁で四捨五入します。
引数説明: X: 四捨五入対象
DigitNumber: 桁数
1:一桁目
2:10の桁
3:100の桁
0:処理しない
-1:少数点第一位(通常のRoundOffと同じ処理)
-2:少数点第二位
-3:少数点第三位
戻り値: 四捨五入後のX
処理できない場合Xの値そのままが入るはず
備考:
履歴: 2001/09/04
//------------------------------}
function RoundOff(X: Extended; DigitNumber: Integer): Extended; overload;
var
CalcDigit: Extended;
begin
Result := X;
if X = 0 then Exit;
case DigitNumber of
0: Exit;
1..High(DigitNumber):
begin
CalcDigit := IntPower(10, DigitNumber);
Result := Roundoff(X / CalcDigit) * CalcDigit;
end;
Low(DigitNumber)..-1:
begin
CalcDigit := IntPower(10, Abs(DigitNumber)-1);
Result := Roundoff(X * CalcDigit) / CalcDigit;
end;
end;
end;
//------------------------------
{-------------------------------
// IsSame
機能: 適当に小さい誤差の範囲内で
浮動小数点値が等しいかどうかを調べる関数
引数説明: A, B: 比較対照
e: ±の誤差
戻り値: true:等しい false:等しくない
備考: Math.SameValueと同じ機能かも
履歴: 2001/09/05
//------------------------------}
function IsSame(A, B: Extended; e: Extended=0): Boolean;
begin
Result := Abs(A-B) <= e;
end;
//------------------------------
procedure testRoundOff;
begin
Check(True, IsSame(150,RoundOff(145, 1),0.000001)); //145を1桁目で四捨五入すると150になる
Check(True, IsSame(150,RoundOff(154, 1),0.000001)); //154を1桁目で四捨五入すると150になる
Check(True, IsSame(140,RoundOff(144, 1),0.000001));
Check(True, IsSame(160,RoundOff(155, 1),0.000001));
Check(True, IsSame(150,RoundOff(150, 1),0.000001));
Check(True, IsSame( 10,RoundOff( 14, 1),0.000001));
Check(True, IsSame( 10,RoundOff( 5, 1),0.000001));
Check(True, IsSame( 0,RoundOff( 4, 1),0.000001));
Check(True, IsSame( 20,RoundOff( 15, 1),0.000001));
Check(True, IsSame( 10,RoundOff( 10, 1),0.000001));
Check(True, IsSame(200,RoundOff(150, 2),0.000001)); //150を2桁目で四捨五入すると200になる
Check(True, IsSame(100,RoundOff(140, 2),0.000001));
Check(True, IsSame(100,RoundOff(149, 2),0.000001)); //149を2桁目で四捨五入すると100になる
Check(True, IsSame(200,RoundOff(249, 2),0.000001));
Check(True, IsSame(100,RoundOff( 50, 2),0.000001));
Check(True, IsSame(1000,RoundOff(1400, 3),0.000001)); //1400を3桁目で四捨五入すると1000になる
Check(True, IsSame(2000,RoundOff(1500, 3),0.000001));
Check(True, IsSame(2000,RoundOff(1600, 3),0.000001));
Check(True, IsSame(1000,RoundOff(1499, 3),0.000001));
Check(True, IsSame(2000,RoundOff(2480, 3),0.000001));
Check(True, IsSame(-150,RoundOff(-145, 1),0.000001)); //-145を1桁目で四捨五入すると-150になる
Check(True, IsSame(-150,RoundOff(-154, 1),0.000001));
Check(True, IsSame(-140,RoundOff(-144, 1),0.000001));
Check(True, IsSame(-160,RoundOff(-155, 1),0.000001));
Check(True, IsSame(-150,RoundOff(-150, 1),0.000001));
Check(True, IsSame(- 10,RoundOff( -14, 1),0.000001));
Check(True, IsSame(- 10,RoundOff( -5, 1),0.000001));
Check(True, IsSame(- 0,RoundOff( -4, 1),0.000001));
Check(True, IsSame(- 20,RoundOff( -15, 1),0.000001));
Check(True, IsSame(- 10,RoundOff( -10, 1),0.000001));
Check(True, IsSame(-200,RoundOff(-150, 2),0.000001));
Check(True, IsSame(-100,RoundOff(-140, 2),0.000001));
Check(True, IsSame(-100,RoundOff(-149, 2),0.000001));
Check(True, IsSame(-200,RoundOff(-249, 2),0.000001));
Check(True, IsSame(-100,RoundOff( -50, 2),0.000001));
Check(True, IsSame(-1000,RoundOff(-1400, 3),0.000001));
Check(True, IsSame(-2000,RoundOff(-1500, 3),0.000001));
Check(True, IsSame(-2000,RoundOff(-1600, 3),0.000001));
Check(True, IsSame(-1000,RoundOff(-1499, 3),0.000001));
Check(True, IsSame(-2000,RoundOff(-2480, 3),0.000001));
Check(True, IsSame(0,RoundOff(0.49, -1),0.000001)); //0.49を小数点1桁目で四捨五入すると0になる
Check(True, IsSame(1,RoundOff(0.50, -1),0.000001)); //0.50を小数点1桁目で四捨五入すると1になる
Check(True, IsSame(1,RoundOff(1.49, -1),0.000001));
Check(True, IsSame(2,RoundOff(1.50, -1),0.000001));
Check(True, IsSame(1.0,RoundOff(1.04, -2),0.000001)); //1.04を小数点2桁目で四捨五入すると1.0になる
Check(TRue, IsSame(1.1,RoundOff(1.05, -2),0.000001)); //1.05を小数点2桁目で四捨五入すると2になる
Check(True, IsSame(1.0001,RoundOff(1.00005000, -5),0.00000001));
Check(True, IsSame(1.0000,RoundOff(1.00004999, -5),0.000001));
Check(True, IsSame(1.000050,RoundOff(1.00005000, -6),0.000001));
Check(True, IsSame(1.000050,RoundOff(1.00004999, -6),0.000001));
end;
────────────────────
余談ですが、最初は
RoundOff関数を使わずに
整数のみで自作四捨五入関数を作ってみていました。
DigitNumberを何桁目を四捨五入するかという指定で
整数を以下のように処理してみています。
if DigitNumber=1 then
begin
if (X mod 10) < 5 then
begin
Result := (X div 10) * 10;
end else
begin
Result := ((X div 10)+1) * 10;
end;
end else
if DigitNumber=2 then
begin
if (X mod 100) < (5*10) then
begin
Result := (X div 100) * 100;
end else
begin
Result := ((X div 100)+1) * 100;
end;
end else
//DigitNumber1,2,3..どれでも通用する処理
ResultDigit := Round(IntPower(10,DigitNumber));
TargetDigit := Round(IntPower(10,DigitNumber-1));
if (X mod ResultDigit) < (5*TargetDigit) then
begin
Result := ( X div ResultDigit ) * ResultDigit;
end else
begin
Result := ((X div ResultDigit)+1) * ResultDigit;
end;
end;
このように工夫していくと
RoundやRoundOff関数が用意されていなくても
四捨五入関数を自作することが出来ていくでしょう。
更に余談ですが四捨五入の方法として
少数点以下の桁だけになりますが
Format関数で四捨五入した実数値を文字列として返す事もできます。
procedure Check(S1, S2: String); overload;
begin
if S1 <> S2 then
ShowMessage('×');
end;
procedure testFormatFloat;
begin
Check('1', Format('%.0f', [1.4]));
Check('2', Format('%.0f', [1.5]));
Check('1', Format('%.0f', [1.49]));
Check('2', Format('%.0f', [1.50]));
Check('1.5', Format('%.1f', [1.49]));
Check('1.5', Format('%.1f', [1.50]));
Check('1.5', Format('%.1f', [1.45]));
Check('1.5', Format('%.1f', [1.54]));
Check('1.4', Format('%.1f', [1.44]));
Check('1.6', Format('%.1f', [1.55]));
Check('1.0001', Format('%.4f', [1.00005000]));
Check('1.0000', Format('%.4f', [1.00004999]));
Check('1.000', Format('%.3f', [1.00005000]));
Check('1.000', Format('%.3f', [1.00004999]));
end;
Format関数の戻り値を StrToFloat を使って変換すると
Extendet型の値を取得することができます。
参考────────────────────
FDelphi Delphi FAQ: 小数の切り捨て・切り上げ・四捨五入
http://delfusa.main.jp/delfusafloor/archive/www.nifty.ne.jp_forum_fdelphi/faq/00126.htm
四捨五入はRoundOffを使う。Roundだと四捨五入にならないので記載が間違っている。
小数点以下の切捨て、切り上げ、四捨五入 - Delphi - sciencesystemグループ
http://sciencesystem.g.hatena.ne.jp/bbs/2/24?fromtreemode=1
FDelphiと同じ問題
About Delphi Tips - 計算
http://www2.big.or.jp/~osamu/Delphi/Tips/key.cgi?key=41#0073.txt
Delphi AcidF loor
http://www.wwlnk.com/boheme/delphi/vbtodel/daf0560.html
About Delphi と同じ実装
くろねこ研究所 - [Delphi] 切り捨て、四捨五入、切り上げ
http://www.blackcatlab.com/article.php/ProgramingFAQ_del0044
詳しくてGood!
|