Determine Number of Decimal Place using BigDecimal - java

I was interested to have the following getNumberOfDecimalPlace function:
System.out.println("0 = " + Utils.getNumberOfDecimalPlace(0)); // 0
System.out.println("1.0 = " + Utils.getNumberOfDecimalPlace(1.0)); // 0
System.out.println("1.01 = " + Utils.getNumberOfDecimalPlace(1.01)); // 2
System.out.println("1.012 = " + Utils.getNumberOfDecimalPlace(1.012)); // 3
System.out.println("0.01 = " + Utils.getNumberOfDecimalPlace(0.01)); // 2
System.out.println("0.012 = " + Utils.getNumberOfDecimalPlace(0.012)); // 3
May I know how can I implement getNumberOfDecimalPlace, by using BigDecimal?
The following code doesn't work as expected:
public static int getNumberOfDecimalPlace(double value) {
final BigDecimal bigDecimal = new BigDecimal("" + value);
final String s = bigDecimal.toPlainString();
System.out.println(s);
final int index = s.indexOf('.');
if (index < 0) {
return 0;
}
return s.length() - 1 - index;
}
The following get printed :
0.0
0 = 1
1.0
1.0 = 1
1.01
1.01 = 2
1.012
1.012 = 3
0.01
0.01 = 2
0.012
0.012 = 3
However, for case 0, 1.0, it doesn't work well. I expect, "0" as result. But they turned out to be "0.0" and "1.0". This will return "1" as result.

Combining Turismo, Robert and user1777653's answers, we've got:
int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
return Math.max(0, bigDecimal.stripTrailingZeros().scale());
}
stripTrailingZeros() ensures that trailing zeros are not counted (e.g. 1.0 has 0 decimal places).
scale() is more efficient than String.indexOf().
A negative scale() represents zero decimal places.
There you have it, the best of both worlds.

This code:
int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
String string = bigDecimal.stripTrailingZeros().toPlainString();
int index = string.indexOf(".");
return index < 0 ? 0 : string.length() - index - 1;
}
... passes these tests:
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.001")), equalTo(3));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.01")), equalTo(2));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.1")), equalTo(1));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.000")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.00")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.0")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.1")), equalTo(1));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.01")), equalTo(2));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.001")), equalTo(3));
... if that is indeed what you want. The other replies are correct, you have to use BigDecimal all the way through for this rather than double/float.

Without having to convert to String, it should be more efficient to use the scale directly:
private int getNumberOfDecimalPlaces(BigDecimal bigDecimal)
{
int scale = bigDecimal.stripTrailingZeros().scale();
return scale>0?scale:0;
}

That should do it
int getNumberOfDecimalPlace(BigDecimal number) {
int scale = number.stripTrailingZeros().scale();
return scale > 0 ? scale : 0;
}

If you really get doubles i recommend formating them first as strings before creating the BigDecimal. At least that has worked for me: How to check if a double has at most n decimal places?
Depending on how many digits you expect you can either use standard formating like
String.valueOf(doubleValue);
or you could use specialised formatting to avoid exponential format
DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);
When you have a BigDecimal you can simply call scale() to get the number of decimal places.

It's not your code that's wrong, but your expectations. double is based on a binary floating point representation and completely unfit for accurately representing decimal fractions. Decimal 0.1 e.g. has an infinite number of digits when represented in binary, thus it gets truncated and when converted back to decimal, you get erros in the least significant digits.
If you use BigDecimal exclusively, your code will work as expected.

Try this:
Math.floor(Math.log(x) / Math.log(10))
0.001 = -3
0.01 = -2
0.1 = -1
1 = 0
10 = 1
100 = 2

How about having a look at the javadoc of BigDecimal. I'm not sure, but I'd give getScale and getPercision a try.

The best way to get a BigDecimal with a specified number of decimal places is by using the setscale method over it. Personally I like to also use the rounding version of the method (see the link below):
http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#setScale(int,%20int)
If you're wanting to get the number of decimal positions that a BigDecimal is currently set at call the associated scale() method.

Best option I have found so far (not needing toString + index):
public static int digitsStripTrailingZero(BigDecimal value)
{
return digits(value.stripTrailingZeros());
}
public static int digits(BigDecimal value)
{
return Math.max(0, value.scale());
}

Michael Borgwardt answer is the correct one. As soon as you use any double or float, your values are already corrupted.
To provide a code example:
System.out.println("0 = " + BigDecimalUtil.getNumberOfDecimalPlace("0")); // 0
System.out.println("1.0 = " + BigDecimalUtil.getNumberOfDecimalPlace("1.0")); // 0
System.out.println("1.01 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.01"))); // 2
System.out.println("1.012 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.012"))); // 3
System.out.println("0.01 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.01")); // 2
System.out.println("0.012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.012")); // 3
System.out.println("0.00000000000000000012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.00000000000000000012")); // 20
And an overloaded version of getNumberOfDecimalPlace so you could use it with BigDecimal or String:
public static int getNumberOfDecimalPlace(String value) {
final int index = value.indexOf('.');
if (index < 0) {
return 0;
}
return value.length() - 1 - index;
}
public static int getNumberOfDecimalPlace(BigDecimal value) {
return getNumberOfDecimalPlace(value.toPlainString());
}

Why not just change your code to get a doubles decimal places?
public static int getNumberOfDecimalPlace(double value) {
//For whole numbers like 0
if (Math.round(value) == value) return 0;
final String s = Double.toString(value);
System.out.println(s);
final int index = s.indexOf('.');
if (index < 0) {
return 0;
}
return s.length() - 1 - index;
}

Related

value not displaying the right result

When I use numbers such as the one store in decimal the output just starts showing weird answers
this is my code
public static void main(String[] args) {
long decimal =444444;
long count = 0;
long a = 0;
long b = 0;
while(decimal != 0)
{
a = decimal%2;
b += a* Math.pow(10, count);
count++;
decimal = decimal/2;
}
System.out.print(b);
}
this is the output that it prints 1101100100000011136 when the right output should be 1101100100000011100 for decimal 444444
now when I input 123456 it prints 11110001001000000 which is right
I must use long for this and without using strings so that is the code that I'm using but I can't find a way to fix it since mathematically it seems to work.
edit: the goal of the code is to display the binary representation of the decimal in called "decimal" without using string or arrays
It because the maximum number a long can hold is 9,223,372,036,854,775,807(I get this from Long.MAX_VALUE) and your b after loop for a while it exceeds that maximum value and cannot hold correct value anymore. there for you will have some unexpected result. So with long type, I'm afraid that your function only corrects with a small value.
UPDATE
Since you don't use array or String to hold the binary result, you can use BigInteger to store the value of binary, it can hold more than long.
As what the other answers has pointed out, you can generate the output into a string instead as follows.
long decimal = 444444;
long a = 0;
String result = "";
while ( decimal != 0 )
{
a = decimal % 2;
result = Long.toString( a ) + result;
decimal = decimal / 2;
}
System.out.print( result );

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.

how to two binary strings, return their sum (also a binary string)

Example:
a = "100"
b = "11"
Return a + b = “111”.
its done by parsing int but when two strings are more then int size then it will not working.
i tried with long :
long a1=Long.parseLong(a,2);
long b1=Long.parseLong(b,2);
long sum=a1+b1;
String ans=Long.toBinaryString(sum);
is there any methods for double??
To exceed the long size you will need BigInteger.
public void test() {
String a = "100";
String b = "11";
BigInteger bA = new BigInteger(a, 2);
BigInteger bB = new BigInteger(b, 2);
System.out.println(a + " + " + b + " = " + bA.add(bB).toString(2));
}
This does not help with double though.
For double you can try this:
double number = 2.2;
Long.toBinaryString(Double.doubleToLongBits(number));
https://stackoverflow.com/a/8272792/6049590
Copied and modified from above Source
public double ConvertToDouble(string str){
long v = 0;
for (int i = str.Length - 1; i >= 0; i--) v = (v << 1) + (str[i] - '0');
return = BitConverter.ToDouble(BitConverter.GetBytes(v), 0);
}
https://social.msdn.microsoft.com/Forums/vstudio/en-US/0ff76c9a-8d8c-46f3-94cc-420f719a14e4/how-to-convert-floatdoubleulong-into-binaryotcalhex-string?forum=netfxbcl
Copied and modified from above Source
public string ConvertToString(double value){
string s = String.Empty;
foreach (byte b in BitConverter.GetBytes(value))
{
s += Convert.ToString(b,2).PadLeft(8,'0'); // for hex. For binary, use 2 and 8. For octal, use 8 and 3
}
return s;
}
And now the last one:
double c = ConvertToDouble(a) + ConvertToDouble(b);
string bitString = ConvertToString(c);
string bitString should be your expected result.
For adding then converting String again :
public String XXX()
{
int a = Double.parseDouble("100.0");
int b = Double.parseDouble("11.0");
return (a + b) + "";
}
For adding then returning :
public double XXX()
{
int a = Double.parseDouble("100.0");
int b = Double.parseDouble("11.0");
return (a + b);
}
Have a nice day!
If the strings are too long to fit any of the standard types and you don't want to use BigInteger, then you can do it the old-fashioned way using the same algorithm you use when adding two numbers by hand, on paper.
For example, if you have "110" and "11", you would write:
110
+ 11
----
Then you start from the right and move left, adding the digits. Your first partial result is "1", in the right-hand column:
110
+ 11
----
1
Next, "1" and "1" is "0", with a carry of "1". So you write the "0" and note that you have a carry. Your partial result is "01", with the carry.
In the third column you add the "1" and the carry, again giving you "0" with a carry. Your partial result is "001" and the carry. In the fourth column you add the carry, giving you the final result, "1001".
To do that in code, the easiest thing is to pad the shorter number with "0" on the left so that it's the same length as the longer number. So in the example above I would have turned the "11" into "011". Then write a loop that will process the strings from right to left. I'm not a Java programmer, but you should be able to get the idea from this pseudocode.
number1 = "110"
number2 = "011"
result = ""
carry = 0
for i = length(number1) downto 0
digit1 = number1[i].ToInt()
digit2 = number2[i].ToInt()
sum = digit1 + digit2 + carry
if (sum % 2) == 0
result = result + "0"
else
result = result + "1"
carry = (result > 1)
end for
if (carry = 1)
result = result + "1"
That's the slow and simple way to do it. You can use a variation of that method to do it much faster. Basically, you take the bits 32 at a time (from right to left), do the addition, output the resulting 32 bits, maintain the carry (if any), and then move left. You can do that with bytes, words, ints, or longs. Say you're doing it with bytes and your numbers are "010101010101010101010101" and "10110100110101001111". You would split them into 8-bit groups:
3 2 1
01010101 01010101 01010101
10110100 11010100 11111100
Grab the two bytes from group 1, convert them to int, add them, and output the first 8 bits of the result. If there is a ninth bit to the result (there can't be more than 9 bits), then it's a carry into the next column of numbers.
Continue that until you've finished the last column of numbers. As I said, you can do that with any integral type (byte, short, int, long) to speed up the basic algorithm I laid out for individual bits.

Converting a decimal number to binary in java not showing leading zeroes

This is my function in Java:
public static String convertFromDecimal(int number, int base)
{
String result = "";
/*
* This while loop will keep running until 'number' is not 0
*/
while(number != 0)
{
result = (number%base) + result; // Appending the remainder
number = number / base; // Dividing the number by the base so we can get the next remainder
}
// If the number is already 0, then the while loop will ignore it, so we will return "0"
if(result == "")
{
return "0";
}
return result;
}
It works fine for numbers that convert to numbers not beginning with 0, if the number is supposed to have a zero at the start, it will not record it, could anyone tell me why?
For example, if I print out
convertFromDecimal(13,2) it returns
1101
Which is correct, but if I print out
convertFromDecimal(461,2), I get
111001101
Where the actual answer is
0000000111001101
So it's the same as my answer without the leading zeroes, if anyone knows why I would appreciate the help, thank you.
EDIT My question is different because I don't want 16 digits, I want the binary number of the given decimal, a calculator like this can explain what I want.
I assume you are looking to format all your answers as shorts (16 bits).
In this case, simply check the length of your current string, and add on zeroes as needed.
int zeroesRemaining = 16 - result.length();
for (int i = 0; i < zeroesRemaining; i++) {
result = "0" + result;
}
Alternatively, if you want to do it faster, use a StringBuilder.
int zeroesRemaining = 16 - result.length();
StringBuilder tempBuilder = new StringBuilder(result);
for (int i = 0; i < zeroesRemaining; i++) {
tempBuilder.insert(0, 0); //inserts the integer 0 at position 0 of the stringbuilder
}
return tempBuilder.toString(); //converts to string format
There is also probably a formatter that could do this, but I don't know of such.
If you want to change the number of zeroes to be the closest integer primitive, just set zeroesRemaining to be the (least power of 2 that is greater than the number of bits) minus (the number of bits).
Since you want fixed lengths for your result, in groups of 8 bits, the easiest way is to append 0 to the front of your result until its length is a multiple of 8.
That is as simple as
wile (result.length() % 8 > 0)
{
result = "0" + result;
}
return result;

Categories