2011-10-20 13 views
2

誰かが与えられた数字に向かって5で終わる数字を四捨五入するアルゴリズムのためにコードを提供してください(どんな言語でもできますが、.Net言語とVB6を書きますか?与えられた数字に向かって丸めます

RoundTo(double値に向かっ倍、numberOfDigitsBehindCommaをINT)

RoundTo(1.25,1,1)= 1.2 RoundTo(1.25,2,1)= 1.3

RoundTo(1.26,1、 1)= 1.3 RoundTo(1.24,2,1)= 1.2

負の数の解を含めてください。

編集:私の要件について混乱が生じるようですが、結果のコードが満たすべきすべてのアサーションを記入します。私の解決策はそうです。

[TestMethod] 
    public void RoundTowards() 
    { 
     double x=3.44;double y=3.45;double z=4.45; 
     double a = 3.51; double b = 4.5001; double c = -1.14; double d = -1.15; 
     var mean=4; 
     Assert.AreEqual(3.4,x.RoundTowards(mean,1)); 
     Assert.AreEqual(3.5, y.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.4, z.RoundTowards(mean, 1)); 
     Assert.AreEqual(3.5, a.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.5, b.RoundTowards(mean, 1)); 
     mean = 5; 
     Assert.AreEqual(3.4, x.RoundTowards(mean, 1)); 
     Assert.AreEqual(3.5, y.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.5, z.RoundTowards(mean, 1)); 
     Assert.AreEqual(3.5, a.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.5, b.RoundTowards(mean, 1)); 
     mean = 3; 
     Assert.AreEqual(3.4, x.RoundTowards(mean, 1)); 
     Assert.AreEqual(3.4, y.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.4, z.RoundTowards(mean, 1)); 
     Assert.AreEqual(3.5, a.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.5, b.RoundTowards(mean, 1)); 
     Assert.AreEqual(Math.Round(-1.1,4),Math.Round(c.RoundTowards(mean, 1),4)); 
     Assert.AreEqual(Math.Round(-1.1,4),Math.Round(d.RoundTowards(mean, 1),4)); 
     mean = -2; 
     Assert.AreEqual(Math.Round(3.4,4),Math.Round(x.RoundTowards(mean, 1),4)); 
     Assert.AreEqual(Math.Round(3.4,4),Math.Round(y.RoundTowards(mean, 1),4)); 
     Assert.AreEqual(Math.Round(4.4,4),Math.Round(z.RoundTowards(mean, 1),4)); 
     Assert.AreEqual(Math.Round(3.5,4),Math.Round(a.RoundTowards(mean, 1),4)); 
     Assert.AreEqual(Math.Round(4.5,4),Math.Round(b.RoundTowards(mean, 1),4)); 
     Assert.AreEqual(Math.Round(-1.1, 4), Math.Round(c.RoundTowards(mean, 1), 4)); 
     Assert.AreEqual(Math.Round(-1.2, 4), Math.Round(d.RoundTowards(mean, 1), 4)); 

    } 

    [TestMethod] 
    public void RoundTowardsTowardZero() 
    { 
     double x = 3.45; double y = -3.45; 
     double a = -3.551; double b = 4.551; double c = 4.5500001; double d = 4.5501; 
     var mean = 0; 
     Assert.AreEqual(3.4, x.RoundTowards(mean, 1)); 
     Assert.AreEqual(-3.4, y.RoundTowards(mean, 1)); 
     Assert.AreEqual(-3.6, a.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.6, b.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.5, c.RoundTowards(mean, 1)); 
     Assert.AreEqual(4.6, d.RoundTowards(mean, 1)); 

    } 

    [TestMethod] 
    public void Test14_55() 
    { 
     Assert.AreEqual((14.55).RoundTowards(9, 1) ,14.5); 
     Assert.AreEqual((14.55).RoundTowards(15,1), 14.6); 
    } 

    [TestMethod] 
    public void Test14_5499999() 
    { 
     Assert.AreEqual((14.54999999).RoundTowards(9, 1) ,14.5); 
     Assert.AreEqual((14.54999999).RoundTowards(15,1), 14.6); 
    } 

ありがとう!!!

答えて

2

ここでのすべての解決策は複雑すぎると思います。あなたの問題は、あなたがちょうど中間点にあるときに丸めの方向を制御できるようにしたいということにあるようです。乗算して10の整数倍で除算するだけで、N桁の小数点を辿る問題を減らすことができるので、小数点以下5桁の場合にこれを修正すれば十分です。数字xを四捨五入して、 0.5は、あなただけの

result = ceil(x - 0.5); 

これらの作業を行う、あなただけの

result = floor(x + 0.5); 

あなたは0.5が0に下向きに丸められるようにXを四捨五入したい場合は

を行い、1に切り上げているので、X = 0.5の場合、 floor(x + 0.5)= floor(1)= 1、ceil(x-0.5)= ceil(0)= 0である。他の番号は常に正しく丸みを帯びていることを確認するには、

x = 0.4: floor(x + 0.5) = floor(0.9) = 0 
     ceil(x - 0.5) = ceil(-0.1) = 0 
x = 0.6: floor(x + 0.5) = floor(1.1) = 1 
     ceil(x - 0.5) = ceil(0.1) = 1 

ので、全体のコードは次のようになります。

double RoundTo(double value, double towards, int digits) { 
    double mult = pow(10, digits); /* to handle variable number of digits */ 
    bool downwards = (towards < value); 
    value *= mult; /* scale */ 
    value = (downwards ? ceil(value - 0.5) /* round midpoint downwards */ 
        : floor(value + 0.5)); /* round midpoint upwards */ 
    return value/mult; /* scale back */ 
} 

このソリューションは、実際の数学ライブラリに全体のプロセスをオフロードして、CPUのALU、そのためであります非常に堅牢です。これは余分な微調整なしで明らかに負の数を処理し、無限大と正確に動作します。

+0

ありがとう、このソリューションは私のものとよく似ていますが、より簡潔です。最後の5つが499999999999または5000000001として格納されている場合は、浮動小数点変数を扱うときによく使用されます。私はこれを、所望の丸め位置よりさらに4つ下の小さな加減算を提供することによって解決した。しかし、これが最善のアプローチであるかどうかは私には分かりません。 – Dabblernl

+0

上記の私の懸念に対処する必要がある場合でも、私はあなたの答えを受け入れました。私はあなたがそれについてコメントする時間を見つけることを願っています。私はあなたのアルゴリズムを変更して、0.5の+/-値を.50001に変更しました。現在、すべてのテストは緑色です。 – Dabblernl

+0

こんにちは、はい、浮動小数点数は扱いにくい獣です。 + 0.50001では、0.499990000に対して不正確な丸めが行われるため、調整係数を追加しません。エラーを許容できない場合は、浮動小数点数を使用しないでください。 10ベースの固定小数点数。 (a、b)のように数字を表し、両方が整数で、数値が(a + b/1000000)であるとします。 –

2

これは仕事をするようですが、少なくとも私のテストは「グリーン」です。 、丸める値の再追加は再び0.99999999999999999999のような典型的なダブルスを出産することができますように私はでコーディングされた0.1

<Extension()> 
    Public Function RoundTo(ByVal value As Double, mean As Double, digitsBehindComma As Integer) As Double 
     Dim correctedValue = value - mean 
     Dim increasedValue = correctedValue * Math.Pow(10, digitsBehindComma) 
     Dim trailingDigitCorrection = Math.Sign(correctedValue) * Math.Pow(10, -4) 'Safeguard against a trailing bit (i.e. 1.50000000001) 
     Dim halfAddition = Math.Sign(correctedValue) * 0.5 
     Dim division = 10^digitsBehindComma 
     Dim sum = increasedValue - trailingDigitCorrection + halfAddition 

     Dim result = Fix(increasedValue - addition + halfAddition)/division 
     Return Math.Round(result + mean, digitsBehindComma) 

    End Function 
+0

これは、1の方に4.55ラウンドと1に向かって4.55ラウンドの両方に対して正しく動作しますか? (私はVBプログラマではないと確信しています) – CygnusX1

+0

はいサー;-) trailingDigitCorrectionはそれを処理します。私は今、この答えの2番目のupvoteに値するのですか? ;-) – Dabblernl

2

の代わりに、最後にもう一度を丸めるために特に必要が好きではありませんストレートC

私は質問を完全に理解しているとは確信していないので、いくつか前提をしました 1.235を丸めていると言いますが、5の前に小数点以下を四捨五入してください。したがって、RoundTo(1.235,2,1)= 1.2であるが、RoundTo(1.235,2,2)= 1.24およびRoundTo(1.235,1,2)= 1.23

負の数の場合は、計算が最も集中しないソリューションですが、わかりやすく修正する必要があります。

#include <cstdlib> 
#include <iostream> 
#include <math.h> 

double round(double value, double toWards, int numberOfDigitsBehindComma) 
{ 
    double value1 = floor(value * pow(10,numberOfDigitsBehindComma)); 
    double value2 = floor(value * pow(10,numberOfDigitsBehindComma + 1)) - value1 * 10; 
    if (fabs(value2) > 5 || (fabs(value2) == 5 && toWards > value)) 
    { 
     value1++; 
    } 
    double value3 = value1/pow(10,numberOfDigitsBehindComma); 
    return value3;  
} 
+0

私は使い方について混乱しています。私はtoWardsパラメータが末尾の5の場合にその数に向かって丸めることを意図していると思いました。14.55は9(Down)に向かってラウンドします。 – 8bitwide

+0

私のテストは、次のアサーションで失敗します:Round(4.551,0,1)== 4.6;ラウンド(14.55,9,1)== 14.6;丸(-2.45、-2,1)== - 2.4; – Dabblernl

+0

@Dabblernlなぜ、0に向かって四捨五入すると、4.551の丸めが4.6になるのですか? – Gareth

2

下記の@ 8bitwideのソリューションをアップデートしています。

EDIT:浮動小数点表現のエラーを処理するために、私はvalue2 == 0.5isHalf(value2)ファジー比較を行うことができる関数に置き換えました。あなたの数値は、私が出席したブリッジトーナメントに基づいて、数千もの低精度値の計算から来ているので、あなたの目的のためには問題ありません。

つまり、数字4.5500000000001が発生した場合、実際には4.5500000000001の代わりに4.55が表示されます。

このテストケースには4.5500001が含まれています。 doubleには約15桁の精度があります。したがって、正確な数値が7桁にしかならない場合は、計算には間違いがあります。

我々の議論に関連して
#include <cstdlib> 
#include <iostream> 
#include <math.h> 

bool isHalf(double x) 
{ 
    return abs(x - 0.5) <= 1e-10; // or whatever degree of fuzziness suits you 
} 

double round(double value, double toWards, int numberOfDigitsBehindComma) 
{ 
    double value0 = value * pow(10,numberOfDigitsBehindComma); 
    double value1 = floor(value0); 
    double value2 = value0 - value1; // 0 <= value2 < 1 
    if (value2 > 0.5 || isHalf(value2) && toWards > value)) 
    { 
     value1++; 
    } 
    double value3 = value1/pow(10,numberOfDigitsBehindComma); 
    return value3;  
} 
+1

ここでは1つのアサーションだけが失敗しますが、それは対処方法がわからない隠された要件です:Round(4.5500001,4,1)== 4.5問題はダブルが本当に4.55でなければならないということですラウンド(4.55,4,1)= 4.5)このような数値は4.550000001と表されることがあります。私はそのような末尾のビットを取り除きたいが、その問題にどのように対処するかについては不明である。私自身のアルゴリズムでは、必要なコンマの後ろの桁数より4桁後の数字を削除します。 – Dabblernl

+0

あなたは単にダブル(またはバイナリで動作する他の浮動小数点数)として4.55を表すことはできません!バイナリシステムで4.55は100.10001100(1100)です。 10進数の1/3のようなものは0.333(3)です。 _any_ポイントでカットすると、丸め戦略に応じて、4.55より少し下または4.55より少し上の値が得られます。 Precise 4.55は、バイナリシステムで動作するコンピュータには表示されません。 – CygnusX1

+0

@ CygnusX1:ソリューションがこれを考慮に入れて、4.54999999999、または4.55000000001を4.55 – Dabblernl

1

[...]それは、私はそれに対処する方法について自分自身がわからないよ隠しrequierementです:ラウンド(4.5500001,4,1)= = 4.5問題は、doubleが本当に4.55でなければならないとき(したがってRound(4.55,4,1)= 4.5)、このような数値は時々4.550000001として表すことができるということです。私はそのような末尾のビットを取り除きたいが、その問題にどのように対処するかについては不明である。私自身のアルゴリズムでは、必要なコンマの後ろの桁数より4桁後の数字を削除します。

ブリッジトーナメントにスコアを付けるために使用されます。コンマの後ろの2桁目の丸め誤差は、チャンピオンシップと2位の差を意味します。

あなたのソフトウェアは時間が重要ではないと推測します。つまり、1秒間に何百万回も計算を実行せず、少し遅くなると、ソフトウェアは使用できなくなります。

浮動小数点数または2倍のバイナリ特性から生じる丸めの問題を回避するために、関連するすべての計算に10進法を使用することをお勧めします。もちろん、計算はバイナリシステムを使用する場合よりも数倍遅くなりますが、計算が正確になるはずです。

Visual Basic 6では、Currencyと呼ばれるタイプがありますが、固定小数点変数です。ドットの後に常に4桁(10進数)を保持します。 VB.NETはDecimal を導入していますが、これは固定ではありませんが、10進システムでも機能します。

私はそれがサポートする数学演算を正確にはわかりませんが、すべての基本演算がそこにあることは間違いありません。より複雑なもの(対数、指数、三角関数)を使うと、いくつかの有害なキャストが必要になるかもしれませんが、橋の中でそれが必要でないことを願っています:)

一度10進数の世界に入ると、丸めの問題なしに丸め関数(例えばxanによって提供される関数)を返します。


代替方法として、どこでも整数を使用できます。ドットの後ろの4桁の数字だけを常に気にかけている場合は、すべての値に10000を掛けて、それらの「拡張された」値で計算を実行してください。乗算を実行するときには注意してください。

0

この赤ちゃんは少し効率が悪く、コーナーケースで動作しないかもしれませんが、4つのテストポイントで正しいことをしているようです。

#include <stdio.h> 
#include <math.h> 

double roundto (double val, double towards, unsigned ndigit, double expected) 
{ 
double up, down, mult, res; 
int dir; 
dir = (val == towards) ? 0 : (val > towards) ? -1 : 1; 

mult = pow(10, ndigit); 
down = floor (val * mult)/mult; 
up = ceil (val * mult)/mult; 
if (val-down == up-val) {;} 
else dir = (val-down < up-val) ? -1 : 1; 
res = dir > 0 ? up : down; 

/* 
fprintf (stderr, "Val=%f Expected=%f: dir=%d Mult=%f Down=%f Up=%f Res = %f\n" 
    , val, expected, dir, mult, down, up, res); 
*/ 

return res; 
} 

int main(void) 
{ 
double result; 

result = roundto(1.25, 1, 1, 1.2); 
printf ("Result = %f\n", result); 

result = roundto(1.25, 2, 1, 1.3); 
printf ("Result = %f\n", result); 


result = roundto(1.26, 1, 1, 1.3); 
printf ("Result = %f\n", result); 

result = roundto(1.24, 2, 1, 1.2); 
printf ("Result = %f\n", result); 

return 0; 
} 
関連する問題