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

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

Related

Decimal separator in long (Java/Spring)

I need to put the decimal separator point in a Long, I have tried in several ways, but I need it to be dynamic since the decimal separator can change, I have tried with DecimalFormat format = new DecimalFormat("###.##"); but this is not dynamic and it doesn't work the way I wanted it to
Example 1
long amount = 123456;
int decimal = 2;
The result should be Double newAmount = 1234.56
Example 2
long amount = 123456;
int decimal = 4;
The result should be Double newAmount = 12.3456
If I understand correctly, this is what you are trying to achieve:
Long amount = 123456;
int decimal = 2;
double newAmount = amount.doubleValue();
newAmount = newAmount / Math.pow(10, decimal);
Use the pow method of java.lang.math to calculate the power of a number.
Be careful to declare your variable as an object of type Long and not a primitive type if you want to use one of its functions.
As suggested, it is even simpler to just use a double variable instead of a long from the start:
double amount = 123456;
int decimal = 2;
amount = amount / Math.pow(10, decimal);
You can get the required number by dividing the given number by 10 ^ decimalPlaces e.g.
public class Main {
public static void main(String[] args) {
// Test
System.out.println(getNum(123456, 2));
System.out.println(getNum(123456, 4));
}
static double getNum(long val, int decimalPlaces) {
return val / Math.pow(10, decimalPlaces);
}
}
Output:
1234.56
12.3456
All the other answers suggest converting to double and then scaling by powers of 10 before displaying. This will result in some unexpected results because of a loss of precision in the scaling operation. For the complete, gory details on why, please read
What Every Computer Scientist Should Know About Floating-Point Arithmetic and
Is Floating Point Broken?
As to your problem, you should be doing the work using BigDecimal. Converting from long (or Long) to BigDecimal does not lose precision, and will always produce the expected results.
BigDecimal even has a method to do the scaling for you:
long amount = 123456;
int decimal = 2;
BigDecimal n = BigDecimal.valueOf(amount).scaleByPowerOfTen(-decimal);
Output:
1234.56

Get reduced fraction from BigDecimal

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.

How to determine whether number is exactly representable in floating-point?

Since float numbers are base-2 numeral system then it's not possible to represent 0.24F directly as the same it's not possible to represent 1/3 in decimal system without recurring decimal period i.e. 1/3=0.3333... or 0.(3).
So the float number 0.24F when printed back to decimal representation is shown as 0.23 with a change due to rounding:
println(0.24F) => 0.23999999463558197021484375
while 0.25F can be shown directly:
println(0.25F) => 0.25
But how can I determine that a number is exactly representable?
isExactFloat(0.25F) ==> true
isExactFloat(0.24F) ==> false
Maybe Java API has already some function to do that?
UPD
Here is a code which shows float numbers in range [-4, 4] with their internal representation:
public class FloatDestructure {
public static void main(String[] args) {
BigDecimal dec = BigDecimal.valueOf(-4000L, 3);
BigDecimal incr = BigDecimal.valueOf(1L, 3);
for (int i = 0; i <= 8000; i++) {
double dbl = dec.doubleValue();
floatDestuct(dbl, dec);
dec = dec.add(incr);
}
}
static boolean isExactFloat(double d) { return d == (float) d; }
static void floatDestuct(double val, BigDecimal dec) {
float value = (float) val;
int bits = Float.floatToIntBits(value);
int sign = bits >>> 31;
int exp = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1);
int mantissa = bits & ((1 << 23) - 1);
float backToFloat = Float.intBitsToFloat((sign << 31) | (exp + ((1 << 7) - 1)) << 23 | mantissa);
boolean exactFloat = isExactFloat(val);
boolean exactFloatStr = Double.toString(value).length() <= 7;
System.out.println(dec.toString() + " " + (double) val + " " + (double) value + " sign: " + sign + " exp: " + exp + " mantissa: " + mantissa + " " + Integer.toBinaryString(mantissa) + " " + (double) backToFloat + " " + exactFloat + " " + exactFloatStr);
}
}
When mantissa is zero then the float is definitely exact. But in other cases like -0.375 or -1.625 it's not so clear.
In general, this is not possible.
As soon as the number is converted to a float or double, it is just an approximation of the number. So your input to isexactfloat() would not be exact...
If you have the exact version of floating point number in e.g. string format, then it would be possible to devise a function that could tell you if the float or double exactly represents the string formatted number or not. See the comment below by Carlos Heurberger on how to implement such a function.
I would like to share this function here.
// Determine whether number is exactly representable in double.
// i.e., No rounding to an approximation during the conversion.
// Results are valid for numbers in the range [2^-24, 2^52].
public static boolean isExactFloat(double val) {
int exp2 = Math.getExponent(val);
int exp10 = (int) Math.floor(Math.log10(Math.abs(val)));
// check for any mismatch between the exact decimal and
// the round-trip representation.
int rightmost_bits = (52 - exp2) - (16 - exp10);
// create bitmask for rightmost bits
long mask = (1L << rightmost_bits) - 1;
// test if all rightmost bits are 0's (i.e., no rounding)
return (Double.doubleToLongBits(val) & mask) == 0;
}
Edit: the above function could be even shorter
public static boolean isExactFloat(double val) {
int exp2 = Math.getExponent(val);
int exp10 = (int) Math.floor(Math.log10(Math.abs(val)));
long bits = Double.doubleToLongBits(val);
// test if at least n rightmost bits are 0's (i.e., no rounding)
return Long.numberOfTrailingZeros(bits) >= 36 - exp2 + exp10;
}
Demo
Create a BigDecimal from it and catch java.lang.ArithmeticException which it will throw if there is a non-terminating decimal expansion.
Java double can only represent terminating binary fractions. Doing the conversion to double may hide issues, so I think it is better to work from the String representation. The conversion to BigDecimal is exact if the String represents a number. So is conversion from float or double to BigDecimal. Here are test functions for exact representation as float or double:
public static boolean isExactDouble(String data) {
BigDecimal rawBD = new BigDecimal(data);
double d = rawBD.doubleValue();
BigDecimal cookedBD = new BigDecimal(d);
return cookedBD.compareTo(rawBD) == 0;
}
public static boolean isExactFloat(String data) {
BigDecimal rawBD = new BigDecimal(data);
float d = rawBD.floatValue();
BigDecimal cookedBD = new BigDecimal(d);
return cookedBD.compareTo(rawBD) == 0;
}
It is not clear whether your issue has to do with precision (representing 0.24 accurately) or recurring numbers, like 1 / 3.0.
In general precision issues will always creep in if you use the conventional floating point representations.
If precision is a real problem for you, you should look at using BigDecimal. While not as flexible as double it has other advantages like arbitrary precision, and you can also control the rounding behaviour in non-exact calculations (like recurring decimal values).
If all you are after is precision control, you might want to look at the Apache Commons Math Precision class.
You could just compare the double and the float?
public static boolean isExactFloat(double d, float f) {
return d == f;
}
Demo

Get a numer decimal part as Integer using only math

Edit: This has to do with how computers handle floating point operations, a fact that every programmer faces once in a lifetime. I didn't understand this correctly when I asked the question.
I know the simplest way to start dealing with this would be:
val floatNumber: Float = 123.456f
val decimalPart = floatNumber - floatNumber.toInt() //This would be 0.456 (I don't care about precision as this is not the main objective of my question)
Now in a real world with a pen and a piece of paper, if I want to "convert" the decimal part 0.456 to integer, I just need to multiply 0.456 * 1000, and I get the desired result, which is 456 (an integer number).
Many proposed solutions suggest splitting the number as string and extracting the decimal part this way, but I need the solution to be obtained mathematically, not using strings.
Given a number, with an unknown number of decimals (convert to string and counting chars after . or , is not acceptable), I need to "extract" it's decimal part as an integer using only math.
Read questions like this with no luck:
How to get the decimal part of a float?
How to extract fractional digits of double/BigDecimal
If someone knows a kotlin language solution, it would be great. I will post this question also on the math platform just in case.
How do I get whole and fractional parts from double in JSP/Java?
Update:
Is there a "mathematical" way to "calculate" how many decimals a number has? (It is obvious when you convert to string and count the chars, but I need to avoid using strings) It would be great cause calculating: decimal (0.456) * 10 * number of decimals(3) will produce the desired result.
Update 2
This is not my use-case, but I guess it will clarify the idea:
Suppose you want to calculate a constant(such as PI), and want to return an integer with at most 50 digits of the decimal part of the constant. The constant doesn't have to be necessarily infinite (can be for example 0.5, in which case "5" will be returned)
I would just multiply the fractional number by 10 (or move the decimal point to the right) until it has no fractional part left:
public static long fractionalDigitsLong(BigDecimal value) {
BigDecimal fractional = value.remainder(BigDecimal.ONE);
long digits;
do {
fractional = fractional.movePointRight(1); // or multiply(BigDecimal.TEN)
digits = fractional.longValue();
} while (fractional.compareTo(BigDecimal.valueOf(digits)) != 0);
return digits;
}
Note 1: using BigDecimal to avoid floating point precision problems
Note 2: using compareTo since equals also compares the scale ("0.0" not equals "0.00")
(sure the BigDecimal already knows the size of the fractional part, just the value returned by scale())
Complement:
If using BigDecimal the whole problem can be compressed to:
public static BigInteger fractionalDigits(BigDecimal value) {
return value.remainder(BigDecimal.ONE).stripTrailingZeros().unscaledValue();
}
stripping zeros can be suppressed if desired
I am not sure if it counts against you on this specific problem if you use some String converters with a method(). That is one way to get the proper answer. I know that you stated you couldn't use String, but would you be able to use Strings within a Custom made method? That could get you the answer that you need with precision. Here is the class that could help us convert the number:
class NumConvert{
String theNum;
public NumConvert(String theNum) {
this.theNum = theNum;
}
public int convert() {
String a = String.valueOf(theNum);
String[] b = a.split("\\.");
String b2 = b[1];
int zeros = b2.length();
String num = "1";
for(int x = 0; x < zeros; x++) {
num += "0";
}
float c = Float.parseFloat(theNum);
int multiply = Integer.parseInt(num);
float answer = c - (int)c;
int integerForm = (int)(answer * multiply);
return integerForm;
}
}
Then within your main class:
public class ChapterOneBasics {
public static void main(String[] args) throws java.io.IOException{
NumConvert n = new NumConvert("123.456");
NumConvert q = new NumConvert("123.45600128");
System.out.println(q.convert());
System.out.println(n.convert());
}
}
output:
45600128
456
Float or Double are imprecise, just an approximation - without precision. Hence 12.345 is somewhere between 12.3449... and 12.3450... .
This means that 12.340 cannot be distinghuished from 12.34. The "decimal part" would be 34 divided by 100.
Also 12.01 would have a "decimal part" 1 divided by 100, and too 12.1 would have 1 divided by 10.
So a complete algorith would be (using java):
int[] decimalsAndDivider(double x) {
int decimalPart = 0;
int divider = 1;
final double EPS = 0.001;
for (;;) {
double error = x - (int)x;
if (-EPS < error && error < EPS) {
break;
}
x *= 10;
decimalPart = 10 * decimalPart + ((int)(x + EPS) % 10);
divider *= 10;
}
return new int[] { decimalPart, divider };
}
I posted the below solution yesterday after testing it for a while, and later found that it does not always work due to problems regarding precision of floats, doubles and bigdecimals. My conclusion is that this problem is unsolvable if you want infinite precision:
So I re-post the code just for reference:
fun getDecimalCounter(d: Double): Int {
var temp = d
var tempInt = Math.floor(d)
var counter = 0
while ((temp - tempInt) > 0.0 ) {
temp *= 10
tempInt = Math.floor(temp)
counter++
}
return counter
}
fun main(args: Array <String> ) {
var d = 3.14159
if (d < 0) d = -d
val decimalCounter = getDecimalCounter(d)
val decimalPart = (d - Math.floor(d))
var decimalPartInt = Math.round(decimalPart * 10.0.pow(decimalCounter))
while (decimalPartInt % 10 == 0L) {
decimalPartInt /= 10
}
println(decimalPartInt)
}
I dropped floats because of lesser precision and used doubles.
The final rounding is also necessary due to precision.

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