Get reduced fraction from BigDecimal - java

I'm doing some really precise decimal calculations that I turn into reduced fractions at the end. The decimals need precision to 96 decimals.
Since the precision is so important I'm using BigDecimal and BigInteger.
The calculation of the BigDecimal always returns the correct decimal value, but my function for turning this decimal into a fraction fails for some cases
Let's say I have a BigDecimal d
d.toString() = 32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
When my function is trying to turn this into a fraction it outputs
Decimal from BigDecimal is:
32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
// Run the BigDecimal into getFraction
Denominator before reducing:
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Numerator before reducing:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
// Reduced fraction turns into:
-1/0
// But should output
290/9
Here's my function for reducing decimal into fraction:
static int[] getFraction(BigDecimal x) {
BigDecimal x1 = x.stripTrailingZeros();
//System.out.println(x.toString() + " stripped from zeroes");
//System.out.println(x1.scale());
// If scale is 0 or under we got a whole number fraction something/1
if(x1.scale() <= 0) {
//System.out.println("Whole number");
int[] rf = { x.intValue(), 1 };
return rf;
}
// If the decimal is
if(x.compareTo(BigDecimal.ZERO) < 0) {
// Add "-" to fraction when printing from main function
// Flip boolean to indicate negative decimal number
negative = true;
// Flip the BigDecimal
x = x.negate();
// Perform same function on flipped
return getFraction(x);
}
// Split BigDecimal into the intval and fractional val as strings
String[] parts = x.toString().split("\\.");
// Get starting numerator and denominator
BigDecimal denominator = BigDecimal.TEN.pow(parts[1].length());
System.out.println("Denominator :" + denominator.toString());
BigDecimal numerator = (new BigDecimal(parts[0]).multiply(denominator)).add(new BigDecimal(parts[1]));
System.out.println("Numerator :" + numerator.toString());
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
}
static int[] reduceFraction(int numerator, int denominator) {
// First find gcd
int gcd = BigInteger.valueOf(numerator).gcd(BigInteger.valueOf(denominator)).intValue();
//System.out.println(gcd);
// Then divide numerator and denominator by gcd
int[] reduced = { numerator / gcd, denominator / gcd };
// Return the fraction
return reduced;
}
If anyone would clarify if I have made any mistakes, I would greatly appreciate it!
** UPDATE **
Changed reduceFraction function:
Now returns a String[] instead of int[]
static String[] reduceFraction(BigDecimal numerator, BigDecimal denominator) {
// First find gcd
BigInteger nu = new BigInteger(numerator.toString());
BigInteger de = new BigInteger(denominator.toString());
BigInteger gcd = nu.gcd(de);
// Then divide numerator and denominator by gcd
nu = nu.divide(gcd);
de = de.divide(gcd);
String[] reduced = { nu.toString(), de.toString() };
// Return the fraction
return reduced;
}
getFraction returns:
// Now we reduce, send BigDecimals for numerator and denominator
return reduceFraction(num, den);
instead of
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
Still gets wrong answer from function
Output fraction now is
// Gcd value
gcd = 1
// Fraction is then:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
//gcd Value should be:
gcd = 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
// Whit this gcd the fraction reduces to:
290/9

// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
Well, this must fail in this case, because neither the numerator or denominator can fit into an int here.
The numerator becomes -1908874353, and the denominator becomes 0 after you call intValue() on them. You must carry with BigIntegers until the end of the computation.
Before converting them to int or long, if you must do so, you can check whether they can be converted to those types without loss of precision by checking them against Integer.MIN_VALUE, Integer.MAX_VALUE, Long.MIN_VALUE, and Long.MAX_VALUE.

You seem to be making this much harder than it needs to be. Here is my initial attempt:
public static BigInteger[] toRational(BigDecimal decimal)
{
int scale = decimal.scale();
if (scale <= 0) {
return new BigInteger[]{decimal.toBigInteger(), BigInteger.ONE};
} else {
BigInteger denominator = BigInteger.TEN.pow(scale);
BigInteger numerator = decimal.unscaledValue();
BigInteger d = numerator.gcd(denominator);
return new BigInteger[]{numerator.divide(d), denominator.divide(d)};
}
}
The rational number is always returned in lowest terms. Note that if decimal is 0 then 0/1 is returned as the rational. If decimal is negative then the rational is returned with the numerator negative.

Related

Convert double into BigRational (two BigInteger for numerator/denominator)

I have a custom made BigRational class in java.
It is implemented as two BigInteger, representing numerator and denominator.
I have a "from string" method that take input in the form "-1234/43"
but I would like to implement a from double/from float;
I'm not scare of generating a very large number, but I would like to keep all the precision present in the floating point representation; thus if I converted them in some decimal representation I would lose precision thanks to rounding.
-How do I generate a pair of BigIntegers that interpreted as numerator/denominator represents the same exact number as a given float/double?
(Hopefully by being in Java I do not need to worry about bigendian/littleendian, but I would like a confermation too)
So, thanks to a good friend I have found a good solution, so I will post it here for anyone in need.
It is not using any string representation so it should also be quite on the fast side.
I have tested it "reasonably" and It seams to work and to keep the exact representation.
Of course, we should still add some 'if' to handle NANs.
final static int mantissaBits=53;
public static BigRational from(double num){
int exponent=Math.getExponent(num);
long man=Math.round(Math.scalb(num, mantissaBits-exponent));
long den=Math.round(Math.scalb(1.0, mantissaBits-exponent));
return new BigRational(BigInteger.valueOf(man),BigInteger.valueOf(den));
}
Caveat: Not all numbers are rational, e.g. PI is not a rational number. However, given that double (and float) have limited precision, there are a limited number of digits in a floating-point value, so you can always find a rational number for that. E.g. Math.PI is a double with the value 3.141592653589793. That number is the rational number 3_141_592_653_589_793 / 1_000_000_000_000_000.
Understanding the caveat that floating-point values aren't accurate, you can find the rational number with the help of BigDecimal, then normalize the rational number using BigInteger.gcd().
Like this:
static void printAsRational(double value) {
printAsRational(BigDecimal.valueOf(value));
}
static void printAsRational(float value) {
printAsRational(new BigDecimal(Float.toString(value)));
}
static void printAsRational(BigDecimal value) {
BigInteger numerator, denominator;
if (value.signum() == 0) {
// Zero is 0 / 1
numerator = BigInteger.ZERO;
denominator = BigInteger.ONE;
} else {
BigDecimal bd = value.stripTrailingZeros(); // E.g. 1.20 -> 1.2
if (bd.scale() < 0)
bd = bd.setScale(0); // E.g. 1.7e3 -> 1700
numerator = bd.unscaledValue(); // E.g. 1.25 -> 125
denominator = BigDecimal.valueOf(1, -bd.scale()).toBigInteger(); // E.g. 1.25 -> 100
// Normalize, e.g. 12/8 -> 3/2
BigInteger gcd = numerator.gcd(denominator);
if (! gcd.equals(BigInteger.ONE)) {
numerator = numerator.divide(gcd);
denominator = denominator.divide(gcd);
}
}
System.out.println(value + " = " + numerator + " / " + denominator);
}
Tests
printAsRational(Math.PI);
printAsRational(Math.E);
printAsRational(1.25);
printAsRational(1);
printAsRational(0);
printAsRational(-1.25);
printAsRational(1.25e9);
printAsRational(1.25e-9);
Output
3.141592653589793 = 3141592653589793 / 1000000000000000
2.718281828459045 = 543656365691809 / 200000000000000
1.25 = 5 / 4
1.0 = 1 / 1
0.0 = 0 / 1
-1.25 = -5 / 4
1.25E+9 = 1250000000 / 1
1.25E-9 = 1 / 800000000

Java Rounding to 15 decimal place

I have the below codes round the forward rate to 15 decimal place. When _ForwardRate is 13,555.0, the result return is wrong.
public double round(double Number, int Decimal_Place) {
if (Number==0) return 0;
double _plug = 0.000001;
if (Number < 0) {
_plug = -0.000001;
}
//Sometime a number is rounded down to 2.22499999999 by java.
//Actual precision is 2.245. Without this plug, a 2 dp rounding result
//in 2.22 when it should be 2.23
double _newNumber = Number;
if (Decimal_Place==2) {
_newNumber = _newNumber+_plug;
}
double _number_abs = Math.abs(_newNumber);
double _factor = Math.pow(10, Decimal_Place);
double _rd = Math.round(_number_abs * _factor);
double _r = _rd/_factor;
if (Number <= 0)
_r = _r * -1;
return _r;
}
Double _ForwardRate = getForward_rate();
BigDecimal _fwdrate_bd = BigDecimal.valueOf(_ForwardRate.doubleValue());
_ForwardRate = round(new Double(_fwdrate_bd.doubleValue()), 15);
Current result
9,223.372036854777
Expected result
13,555.000000000000000
Your problem is that Math.round(double a) returns long, and you're overflowing.
One easy way to do this, is to use BigDecimal:
public static double round(double number, int decimalPlaces) {
return BigDecimal.valueOf(number)
.setScale(decimalPlaces, RoundingMode.HALF_UP)
.doubleValue();
}
This allows you to control the rounding mode. Note that the rounding done by Math.round() is a HALF_CEILING which isn't supported by setScale().
You might want to consider doing all you math using BigDecimal, if you need that level of precision.
Consider:
double _number_abs = Math.abs(_newNumber);
At this point, _number_abs contains the value 13555.0
double _factor = Math.pow(10, Decimal_Place);
Now _factor contains 1.0E15
double _rd = Math.round(_number_abs * _factor);
According to the Javadoc
Math.round() Returns the closest long to the argument, with ties rounding to positive infinity.
Since _number_abs * _factor is 1.3555E19, which is larger than Long.MAX_VALUE, the result is Long.MAX_VALUE, i.e. the "closest" Long to the given value.

7 fractions sorted in an array, java?

So here is my problem. I need help trying to figure out what I'm doing wrong and go from there. I need to create a program that runs these instructions.
Create java class named Fraction. This class is used to represent a ratio of two integers. Include mutator methods that allow the user to set the numerator and the denominator. Also include a method to display the fraction on the screen as a ration (e.g. 5/9). This method does not need to reduce the fraction to lowest terms.
The fraction class should contain the following:
• Private instance variables to store the numerator, denominator, and the ratio_value.
• Constructor(s) that set all of the instance variables.
• Public methods to get and set the instance variables.
• A public method named reduce( ) that returns lowest terms of a fraction.
• A public method named toString( ) that returns a String containing the fraction as a ratio.
• A private method name gcd() that return the greatest common divisor of two integers.
Create a test program that allows the user to create array of 7 fractions. Then the program will sort the fraction in ascending order. The highest and the lowest fractions are thrown away and the remaining fractions are added together. The program should display all the fractions and their sum. The sum should be reduced to lowest terms and displayed on the screen. For example, if the sum is 20/60, the program should display 1/3.
Write a sort method in the test program to sort the array of fractions and calculate the sum.
Assume you have the following 7 fractions: 6/7, 2/4, 3/4, 3/18, 1/8, 10/20, 2/6, then an example of the output after throwing away the lowest and the largest fractions, will be:
3 / 18 + 2 / 6 + 2 / 4 + 10 / 20 + 3 / 4 = 9 / 4
I completely lost on how to solve this and stuck where I am at. Below is a copy of my class and .main file. I have some different things that I have tried commented out with '//' so sorry for the long codes in advance. I've tried over and over to figure this and have been stuck for days. It keeps giving me this weird error called null.
How can I get this to work? Thanks.
import java.io.*;
import java.util.*;
public class Arrays_hw5 {
private static final Scanner keyb = null;
public static void main(String[] args) {
Fraction [] fr = new Fraction[7];
String reduce = "";
int numerator = 0, denominator = 0;
Scanner keyb = null;
FileInputStream fis = null;
//Scanner keyb = new Scanner(System.in);
try {
fis = new FileInputStream(new File("Fraction"));
keyb = new Scanner(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();}
for (int i = 0; i < fr.length; i++) {
//System.out.println("Enter numerator then denominator, hit enter after each entry: ");
// fr[i] = new Fraction(i, i);
// fr[i].getNumerator(keyb.nextInt());
// fr[i].denominator(keyb.nextInt());
System.out.print(fr[i] + " "); }}
public static void selectionSort(int[]arr)
{
int smallest = 0;
for (int outer = 0; outer < arr.length - 1; outer++)
{
smallest = outer;
for(int inner = outer + 1; inner < arr.length; inner++)
{
if (arr[inner] < arr[smallest])
smallest = inner;
}
int v = arr[outer];
arr[outer] = arr[smallest];
arr[smallest] = v; }
}
}
Here is the Fraction Class.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Fraction {
public int numerator = 1;
public int denominator = 1;
public int gcd;
public Fraction() {
super();
}
public Fraction(int n, int d) {
numerator = n;
denominator = d;
}
public int getNumerator() {
return numerator;
}
public void setNumerator(int numerator) {
this.numerator = numerator;
}
public int getDenominator() {
return denominator;
}
public void setDenominator(int denominator) {
this.denominator = denominator;
}
private static int gcd(int numerator, int denominator) {
return denominator == 0 ? numerator : gcd(denominator, numerator % denominator);
}
public double decimal(double numerator, double denominator) {
return numerator / denominator;
}
public static String reduce(int numerator, int denominator) {
int gcd = gcd(numerator, denominator);
return (numerator / gcd) + "/" + (denominator / gcd);
}
#Override
public String toString() {
return numerator + "/" + denominator;
}}
Prepare yourself for a long answer. First it is always best to plan out a project before doing any code so here are the steps we will take: 1) Read input from user and store this input into an array of Fractions. 2) Sort this Fraction array then remove (ignore) highest and lowest values in array. 3) Sum all values of array excluding highest and lowest values, reduce this sum, and print this sum to the screen.
Step 1) Read User Input
You are on the right track but you do not read the user input properly. First, you for some reason open a file. This is unnecessary as you only need to read from the command line. Second, you do not read nor store the input properly. I would suggest repeatedly prompting the user to input a numerator then a denominator. And for every time you read one numerator and one denominator, store these values as a Fraction before you re-prompt the user for input. See the following code block to understand what I mean:
Fraction[7] fractions = new Fraction[7]; // array that will hold our fractions
Scanner inputScanner = new Scanner(System.in); // scanner that takes input from the command line
...
public void readInput()
{
int tempNumer; // variable to store numerator on each iteration
int tempDenom; // variable to store denominator on each iteration
for (int i = 0; i < fractions.length; i++)
{
System.out.println("Enter a numerator:");
tempNumer = inputScanner.nexInt(); // store the user-inputted numerator
System.out.println("Enter a denominator");
tempDenom = inputScanner.nextInt(); // store the user-inputted denominator
fractions[i] = new Fraction(tempNumer, tempDenom); // store a Fraction from our temp variables into our fractions array
}
return;
}
Upon completion of this method, the fractions array will be full of Fraction objects in the order that the user inputted.
Step 2) Sort the fractions array
So, how do we know if a fraction, in normal math, is larger than another fractions? Well, we convert both fractions to have the gcd as each individual fraction's denominator, then we compare the numerators (btw, your gcd method is so wrong. The gcd is between two fraction's denominators, NOT the numerator and denominator of one fraction). The larger numerator at this point is the larger fractions. So, it would be easiest to have a method called fracCompare that takes in two fractions, converts both of them, then return which fraction is larger. We can do this as follows:
public int fracCompare(Fraction fracOne, Fraction fracTwo)
{
// First, find the gcd of the fractions
int gcd = gcd(fracOne.getDenominator, fracTwo.getDenominator);
// Now, we need to convert the numerator accordingly
// We will do this by finding the factor by which the denominator is
// increased and multiply the numerator by this factor
int factorOne = gcd / fracOne.getDenominator();
int tempFracOneNum = fracOne.getNumerator() * factorOne;
int factorTwo = gcd / fracTwo.getDenominator();
int tempFracTwoNum = fracTwo.getNumerator() * factorTwo;
// Now we compare these numerators
// We will return 1 if fracOne is greater than fracTwo
// We will return 2 if fracTwo is greater than fracOne
// We will return 0 if they are the same
if (tempFracOneNum > tempFracTwoNum)
return 1;
else if (tempFracTwoNum > tempFracOneNum)
return 2;
else
return 0;
}
public int gcd(int firstNum, int secondNum)
{
int a = firstNum.getDenominator();
int b = secondNum.getDenominator();
while(a != 0 && b != 0) // until either one of them is 0
{
int c = b;
b = a % b;
a = c;
}
return a+b; // either one is 0, so return the non-zero value
}
A note: I blatantly stole this gcd method from The user, Rushil's answer on another post. Now that we have a compare method, we can sort the array. I will leave the sorting to you because I'm getting sick of formatting code but here is some bubble sort pseudocode to get you started:
i = 0
loop until i = fractions.length - 1
j = i
loop until j = fractions.length - 1
if fraction at j > fraction at j+1
swap fraction at j and fraction at j+1
Step 3) Sum all Fractions in fractions array
Finally at the last step. In order to add fractions we again need to use the gcd. We use this gdc to see what to increase the numerator of both adding fractions by. We will then take this sum and add it to all the other values in the array. Finally, we will reduce the large sum.
int tempGcd;
int tempFactorOne;
int tempFactorTwo;
Fraction sum = fractions[1];
for (int i = 2; i < fractions.length - 2; i++) // we loop from 2 to fractions.length-2 because
// we ignore the least and greatest values in the array
// and we assigned the initial sum to the first fraction
{
tempGcd = gcd(sum.getDenominator(), fractions[i].getDenominator());
tempFactorOne = tempGcd / sum.getDenominator();
tempFactorTwo = tempGcd / fractions[i].getDenominator();
sum.setNumerator(tempFactorOne * sum.getNumerator() + tempFactorTwo * fractions[i].getNumerator()); // add the numerators and store as the sum
sum.setDenominator(gcd); // obviously the denominator is the gcd
}
Hopefully this all should work. I'm sick of typing so I'll leave the reducing of the fraction to you. It is pretty simple--you just need to find the greatest common divisor of the numerator and denominator and then divide both by that divisor. Sorry if my exact code doesn't compile, I'm too lazy to do it myself and you shouldn't be plagiarizing for a school project anyway.

Floating point errors

I am having trouble with floating points. A the double . 56 in Java, for example, might actually be stored as .56000...1.
I am trying to convert a decimal to a fraction. I tried to do this using continued fractions
Continuous Fractions
but my answers using that method were inaccurate due to how to computer stored and rounded decimals.
I tried an alternative method:
public static Rational rationalize(double a){
if(a>= 1){
//throw some exception
}
String copOut = Double.toString(a);
int counter = 0;
System.out.println(a);
while(a%1 != 0 && counter < copOut.length() - 2){
a *= 10;
counter++;
}
long deno = (long)Math.pow(10,counter);//sets the denominator
Rational frac = new Rational((long)a,deno)//the unsimplified rational number
long gcd = frac.gcd();
long fnum = frac.getNumer();//gets the numerator
long fden = frac.getDenom();//gets the denominator
frac = new Rational(fnum/gcd, fden/gcd);
return frac;
}
I am using the string to find the length of the decimal to determine how many time I should multiply by 10. I later truncate the decimal. This gets me the right answer, but it does not feel like the right approach?
Can someone suggest the 'correct' way to do this?
Actually you are doing great.. But this will fail if the Input is something about 11.56. Here you need to to do copOut.length() - 3.
To make it dynamic use String#split()
String decLength = copOut.split("\\.")[1]; //this will result "56" (Actual string after decimal)
Now you just need to do only
while(a%1 != 0 && counter < decLength.length()){
a *= 10;
counter++;
}
If you want to remove the loop then use
long d = (long)Math.pow(10,decLength.length());
a=a*d;

round BigDecimal to nearest 5 cents

I'm trying to figure out how to round a monetary amount upwards to the nearest 5 cents. The following shows my expected results
1.03 => 1.05
1.051 => 1.10
1.05 => 1.05
1.900001 => 1.10
I need the result to be have a precision of 2 (as shown above).
Update
Following the advice below, the best I could do is this
BigDecimal amount = new BigDecimal(990.49)
// To round to the nearest .05, multiply by 20, round to the nearest integer, then divide by 20
def result = new BigDecimal(Math.ceil(amount.doubleValue() * 20) / 20)
result.setScale(2, RoundingMode.HALF_UP)
I'm not convinced this is 100% kosher - I'm concerned precision could be lost when converting to and from doubles. However, it's the best I've come up with so far and seems to work.
Using BigDecimal without any doubles (improved on the answer from marcolopes):
public static BigDecimal round(BigDecimal value, BigDecimal increment,
RoundingMode roundingMode) {
if (increment.signum() == 0) {
// 0 increment does not make much sense, but prevent division by 0
return value;
} else {
BigDecimal divided = value.divide(increment, 0, roundingMode);
BigDecimal result = divided.multiply(increment);
return result;
}
}
The rounding mode is e.g. RoundingMode.HALF_UP. For your examples, you actually want RoundingMode.UP (bd is a helper which just returns new BigDecimal(input)):
assertEquals(bd("1.05"), round(bd("1.03"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.10"), round(bd("1.051"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.05"), round(bd("1.05"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.95"), round(bd("1.900001"), bd("0.05"), RoundingMode.UP));
Also note that there is a mistake in your last example (rounding 1.900001 to 1.10).
I'd try multiplying by 20, rounding to the nearest integer, then dividing by 20. It's a hack, but should get you the right answer.
I wrote this in Java a few years ago: https://github.com/marcolopes/dma/blob/master/org.dma.java/src/org/dma/java/math/BusinessRules.java
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
*/
public static BigDecimal round(BigDecimal value, BigDecimal rounding, RoundingMode roundingMode){
return rounding.signum()==0 ? value :
(value.divide(rounding,0,roundingMode)).multiply(rounding);
}
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
* Example: 5, 10 = 10
*<p>
* HALF_UP<br>
* Rounding mode to round towards "nearest neighbor" unless
* both neighbors are equidistant, in which case round up.
* Behaves as for RoundingMode.UP if the discarded fraction is >= 0.5;
* otherwise, behaves as for RoundingMode.DOWN.
* Note that this is the rounding mode commonly taught at school.
*/
public static BigDecimal roundUp(BigDecimal value, BigDecimal rounding){
return round(value, rounding, RoundingMode.HALF_UP);
}
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
* Example: 5, 10 = 0
*<p>
* HALF_DOWN<br>
* Rounding mode to round towards "nearest neighbor" unless
* both neighbors are equidistant, in which case round down.
* Behaves as for RoundingMode.UP if the discarded fraction is > 0.5;
* otherwise, behaves as for RoundingMode.DOWN.
*/
public static BigDecimal roundDown(BigDecimal value, BigDecimal rounding){
return round(value, rounding, RoundingMode.HALF_DOWN);
}
Here are a couple of very simple methods in c# I wrote to always round up or down to any value passed.
public static Double RoundUpToNearest(Double passednumber, Double roundto)
{
// 105.5 up to nearest 1 = 106
// 105.5 up to nearest 10 = 110
// 105.5 up to nearest 7 = 112
// 105.5 up to nearest 100 = 200
// 105.5 up to nearest 0.2 = 105.6
// 105.5 up to nearest 0.3 = 105.6
//if no rounto then just pass original number back
if (roundto == 0)
{
return passednumber;
}
else
{
return Math.Ceiling(passednumber / roundto) * roundto;
}
}
public static Double RoundDownToNearest(Double passednumber, Double roundto)
{
// 105.5 down to nearest 1 = 105
// 105.5 down to nearest 10 = 100
// 105.5 down to nearest 7 = 105
// 105.5 down to nearest 100 = 100
// 105.5 down to nearest 0.2 = 105.4
// 105.5 down to nearest 0.3 = 105.3
//if no rounto then just pass original number back
if (roundto == 0)
{
return passednumber;
}
else
{
return Math.Floor(passednumber / roundto) * roundto;
}
}
In Scala I did the following (Java below)
import scala.math.BigDecimal.RoundingMode
def toFive(
v: BigDecimal,
digits: Int,
roundType: RoundingMode.Value= RoundingMode.HALF_UP
):BigDecimal = BigDecimal((2*v).setScale(digits-1, roundType).toString)/2
And in Java
import java.math.BigDecimal;
import java.math.RoundingMode;
public static BigDecimal toFive(BigDecimal v){
return new BigDecimal("2").multiply(v).setScale(1, RoundingMode.HALF_UP).divide(new BigDecimal("2"));
}
Based on your edit, another possible solution would be:
BigDecimal twenty = new BigDecimal(20);
BigDecimal amount = new BigDecimal(990.49)
// To round to the nearest .05, multiply by 20, round to the nearest integer, then divide by 20
BigDecimal result = new BigDecimal(amount.multiply(twenty)
.add(new BigDecimal("0.5"))
.toBigInteger()).divide(twenty);
This has the advantage, of being guaranteed not to lose precision, although it could potentially be slower of course...
And the scala test log:
scala> var twenty = new java.math.BigDecimal(20)
twenty: java.math.BigDecimal = 20
scala> var amount = new java.math.BigDecimal("990.49");
amount: java.math.BigDecimal = 990.49
scala> new BigDecimal(amount.multiply(twenty).add(new BigDecimal("0.5")).toBigInteger()).divide(twenty)
res31: java.math.BigDecimal = 990.5
For this test to pass :
assertEquals(bd("1.00"), round(bd("1.00")));
assertEquals(bd("1.00"), round(bd("1.01")));
assertEquals(bd("1.00"), round(bd("1.02")));
assertEquals(bd("1.00"), round(bd("1.024")));
assertEquals(bd("1.05"), round(bd("1.025")));
assertEquals(bd("1.05"), round(bd("1.026")));
assertEquals(bd("1.05"), round(bd("1.049")));
assertEquals(bd("-1.00"), round(bd("-1.00")));
assertEquals(bd("-1.00"), round(bd("-1.01")));
assertEquals(bd("-1.00"), round(bd("-1.02")));
assertEquals(bd("-1.00"), round(bd("-1.024")));
assertEquals(bd("-1.00"), round(bd("-1.0245")));
assertEquals(bd("-1.05"), round(bd("-1.025")));
assertEquals(bd("-1.05"), round(bd("-1.026")));
assertEquals(bd("-1.05"), round(bd("-1.049")));
Change ROUND_UP in ROUND_HALF_UP :
private static final BigDecimal INCREMENT_INVERTED = new BigDecimal("20");
public BigDecimal round(BigDecimal toRound) {
BigDecimal divided = toRound.multiply(INCREMENT_INVERTED)
.setScale(0, BigDecimal.ROUND_HALF_UP);
BigDecimal result = divided.divide(INCREMENT_INVERTED)
.setScale(2, BigDecimal.ROUND_HALF_UP);
return result;
}
public static BigDecimal roundTo5Cents(BigDecimal amount)
{
amount = amount.multiply(new BigDecimal("2"));
amount = amount.setScale(1, RoundingMode.HALF_UP);
// preferred scale after rounding to 5 cents: 2 decimal places
amount = amount.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
return amount;
}
Note that this is basically the same answer as John's.
public static void roundUp()
{
try
{
System.out.println("Enter the currency : $");
Scanner keyboard = new Scanner(System.in);
String myint = keyboard.next();
if (!isEmptyOrBlank(myint).booleanValue())
{
BigDecimal d = new BigDecimal(myint);
System.out.println("Enter the round up factor: $");
String roundUpFactor = keyboard.next();
if (!isEmptyOrBlank(roundUpFactor).booleanValue())
{
BigDecimal scale = new BigDecimal(roundUpFactor);
BigDecimal y = d.divide(scale, MathContext.DECIMAL128);
BigDecimal q = y.setScale(0, 0);
BigDecimal z = q.multiply(scale);
System.out.println("Final price after rounding up to " + roundUpFactor + " is : $" + z);
System.out.println("Want to try with other price Y/N :");
String exit = keyboard.next();
if ((!isEmptyOrBlank(exit).booleanValue()) && ("y".equalsIgnoreCase(exit))) {
roundUp();
} else {
System.out.println("See you take care");
}
}
}
else
{
System.out.println("Please be serious u r dealing with critical Tx Pricing");
}
}
catch (Exception e)
{
System.out.println("Please be serious u r dealing with critical Tx Pricing enter correct rounding off value");
}
}
Tom has the right idea, but you need to use BigDecimal methods, since you ostensibly are using BigDecimal because your values are not amenable to a primitive datatype. Something like:
BigDecimal num = new BigDecimal(0.23);
BigDecimal twenty = new BigDecimal(20);
//Might want to use RoundingMode.UP instead,
//depending on desired behavior for negative values of num.
BigDecimal numTimesTwenty = num.multiply(twenty, new MathContext(0, RoundingMode.CEILING));
BigDecimal numRoundedUpToNearestFiveCents
= numTimesTwenty.divide(twenty, new MathContext(2, RoundingMode.UNNECESSARY));
You can use plain double to do this.
double amount = 990.49;
double rounded = ((double) (long) (amount * 20 + 0.5)) / 20;
EDIT: for negative numbers you need to subtract 0.5

Categories