他の人が指摘したように、正しい答えは、またはのいずれDecimalFormat
かを使用することBigDecimal
です。浮動小数点には小数点以下の桁数がないため、最初から特定の数に丸めたり切り捨てたりすることはできません。10 進数で作業する必要があり、それがこれら 2 つのクラスが行うことです。
次のコードを、このスレッドのすべての回答に対する反例として投稿しています。実際、StackOverflow (および他の場所) 全体で、乗算、切り捨て、除算を推奨しています。この手法の支持者は、次のコードが 92% 以上のケースで間違った出力を生成する理由を説明する義務があります。
public class RoundingCounterExample
{
static float roundOff(float x, int position)
{
float a = x;
double temp = Math.pow(10.0, position);
a *= temp;
a = Math.round(a);
return (a / (float)temp);
}
public static void main(String[] args)
{
float a = roundOff(0.0009434f,3);
System.out.println("a="+a+" (a % .001)="+(a % 0.001));
int count = 0, errors = 0;
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
int scale = 2;
double factor = Math.pow(10, scale);
d = Math.round(d * factor) / factor;
if ((d % 0.01) != 0.0)
{
System.out.println(d + " " + (d % 0.01));
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
}
このプログラムの出力:
10001 trials 9251 errors
編集:以下のいくつかのコメントに対処するために、次のようにモジュラス操作を使用BigDecimal
して、テスト ループのモジュラス部分を再編集しました。new MathContext(16)
public static void main(String[] args)
{
int count = 0, errors = 0;
int scale = 2;
double factor = Math.pow(10, scale);
MathContext mc = new MathContext(16, RoundingMode.DOWN);
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
d = Math.round(d * factor) / factor;
BigDecimal bd = new BigDecimal(d, mc);
bd = bd.remainder(new BigDecimal("0.01"), mc);
if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
{
System.out.println(d + " " + bd);
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
結果:
10001 trials 4401 errors