I found that a rounding error with my Java application. The method used to round was:
public static double round(double value,double precision)
{
return Math.round(value * precision) / precision;
}
This could have an error (i.e. round(138.515,100) should return 138.52, and returns 138.51)
So I've created the following rounder:
// Mikeldi's rounder
public static double round2DecimalPlaces(double value,int decimalPlaces)
{
int s = value<0?-1:1;
double p = 1;
for (int i = 0; i < decimalPlaces; i++) {
p*=10;
}
double n = (long) value;
double d = ((value*10*p)-(n*10*p));
d +=s*5;
d /= 10;
d = (long)d;
d /= p;
return d+n;
}
I created this method since other rounding methods added too much latency to the system (low latency system). This one is around 10 times faster than the previous.
Note: This rounder will only use to round to possitive decimalPlaces (or 0).
Is there any problems I haven't see with this new rounder?
Thanks,
The Math#round method is not broken. 138.515 can't be exactly represented as a double. To see the exact value, you can use:
System.out.println(new BigDecimal(138.515d));
which prints:
138.5149999999999863575794734060764312744140625
It is therefore accurate for round to return 138.51. If you need more precision than double can give, you can use BigDecimal.
EDIT
If BigDecimal is not an option, and if the number of decimals is smallish (say 3 or 4 because these are prices for example), you can use longs instead with the last 4 digits being the decimals. So 138.51d would be 1385100L instead.
Related
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
I am trying to make my number move the first character to the end.
For example I would have my double d.
double d = 12345.6;
double result = 2345.61;
Currently I am removing the first character of d with:
d = d % (int) Math.pow(10, (int) Math.log10(d));
But I do not know how to store the character I am removing so I could put it at the end. I know I could just convert d into an array or a string, but I want to keep it as a double if at all possible.
I am getting my double from a nanosecond clock using Instant.now, so I can guarantee it starts as an 8 digit positive int, which I start by adding .0 to so I can make it a double. I know I can just use string (as I mentioned in the post), but I was wondering if there was a way to do it without conversions.
Thanks for helping!
(this is my first post I apologize if it is bad)
This is a pretty tough problem, and this isn't a working answer but it is pretty close. The only trouble I ran into was java adding decimals to the end of my doubles because it can't represent a number very well. My solution might get you on the right path. The appendChar was messing up because it was adding digits to the end of the double, other than that problem this would have worked.
public static void main(String[] args) {
double test = 4234.1211;
boolean hasDecimals = test % 1 > 0;
double[] leadChar = getLeadDigitAndMulti(test);
double appendedValue = appendLeadChar(test, (int) leadChar[1], hasDecimals);
}
public static double[] getLeadDigitAndMulti(double value) {
double[] result = new double[2];
int multiplier = 10;
int currentMultiplier = 1;
while (value > currentMultiplier) {
currentMultiplier *= multiplier;
}
currentMultiplier /= multiplier;
double trail = value % (currentMultiplier);
result[0] = currentMultiplier;
double lead = value - trail;
lead /= currentMultiplier;
result[1] = lead;
return result;
}
public static double appendLeadChar(double value, int leadChar, boolean hasDecimals) {
if (!hasDecimals) {
return (value * 10) + leadChar;
}
int multiplier = 10;
double currentMultiplier = 1;
while (value > 0) {
value %= currentMultiplier;
currentMultiplier = currentMultiplier / multiplier;
}
return 0;
}
I would start with at least figuring out how many digits long your double is. I'm not too sure of how to make it work but I saw another question that would answer it. Here is the link to that thread:
Number of decimal digits in a double
And the closest code on there that I could find to find the length would be:
double d= 234.12413;
String text = Double.toString(Math.abs(d));
int integerPlaces = text.indexOf('.');
int decimalPlaces = text.length() - integerPlaces - 1;
NOTE THIS SEGMENT OF CODE IS NOT MINE, credit goes to Peter Lawrey who originally answered this in the linked thread.
Then using code or modified code similar to above ^ assuming you would find how to find the length of the double or the total number of digits of your double, you would create a new int or double variable that would store the double and round it using BigDecimal to the highest place value. The place that the BigDecimal will round to will be set using a 10*pow(x) where x is the number of places AFTER the decimal.
Sorry I am unable to provide code on how to make this actually run because I am pretty new to java. Here is what I mean in more detailed pseudocode:
double d = 12345.6; //the double you are evaluating
double dNoDecimal; //will be rounded to have no decimal places in order to calculate place of the 1st digit
double dRoundedFirstDigit; //stores the rounded number with only the 1st digit
double firstDigit; //stores first digit
dNoDecimal = d; //transfers value
dNoDecimal = [use BigDecimal to round so no decimal places will be left]
//dNoDecimal would be equal to 12345 right now
[use above code to find total amount of place values]
[knowing place values use BigDecimal to round and isolate 1st digit of double]
dRoundedFirstDigit = dNoDecimal; //transfers value
dRoundedFirstDigit = [round using BigDecimal to leave only 1st digit]
firstDigit = [dRoundedFirstDigit cut down to 1st digit value using length found from before, again an equation will be needed]
//Now use reverse process
//do the same thing as above, but find the number of decimals by rounding down to leave only decimals (leaving only .6)
//find the length
//use length to calculate what to add to the original value to move the digit to the end (in this case would be d += firstDigit*Math.pow(10,-2))
I apologize if I just made everything seem more confusing, but I tried my best, hope this helps.
Also, doing this without an array or string is really difficult... may I ask why you need to do it without one?
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.
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.
I have a method that returns the factorial of the input. It works perfectly for integers, but I cant figure out how to make it work with decimal numbers.
Here is my method currently:
public static double factorial(double d)
{
if (d == 0.0)
{
return 1.0;
}
double abs = Math.abs(d);
double decimal = abs - Math.floor(abs);
double result = 1.0;
for (double i = Math.floor(abs); i > decimal; --i)
{
result *= (i + decimal);
}
if (d < 0.0)
{
result = -result;
}
return result;
}
I found an implementation but the code wasn't shown (I lost the link) and the example given was 5.5! = 5.5 * 4.5 * 3.5 * 2.5 * 1.5*0.5! = 287.885278
So from this pattern, I just added the decimal value to i in the for-loop result *= (i + decimal)
But clearly my logic is flawed
Edit: Just realsed that the last value is 0.5!, not 0.5. This makes all the difference. So 0.5! = 0.88622 and 5.5! = 5.5 * 4.5 * 3.5 * 2.5 * 1.5 * 0.88622 which equals 287.883028125
The gamma function (which generalizes factorials to real numbers) is rather tricky to implement directly. Use a library such as apache-commons-math to calculate it for you, or look at their source to get a feel of what is involved. Once available, use as follows:
public static double generalizedFactorial(double d) {
// Gamma(n) = (n-1)! for integer n
return Gamma.gamma(d+1);
}
Outputs:
4.0! = 24.0
5.5! = 287.88527781504433
6.0! = 720.0
Previous answer (provides a factorial-like interpretation for real numbers > 1; but since there is already an aggreed-upon extension of factorial to real numbers, please disregard this for anything practical):
public static double f(double d) {
double r = d - Math.floor(d) + 1;
for (;d>1; d-=1) {
r *= d;
}
return r;
}
Outputs:
4.0! = 24.0
5.5! = 487.265625
6.0! = 720.0
Factorial isn't actually defined for x€R, only for x€N. In words: you can't calculate the factorial of a decimal. (You can for 5.0, but not 5.1)
Edit: Thats the view for "traditional" factorial, for (really rarely needed) decimal factorial, see "gamma function".