BigDecimal formatting [closed] - java

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I'd like to format a BigDecimal in Java to 8 Characters (including the seperator), rounding HALF-UP.
Some examples:
12345.6789 -> "12345.68"
123 -> " 123.0"
0.12345678 -> "0.123457"
1.5 -> " 1.5"
0.0045 -> " 0.0045"
12 -> " 12.0"
The result must have leading spaces to fill up to 8 characters.
What's the easiest way?

I'm pretty sure this is not the best way, but it's one way.
First note that if your numbers have more than six digits before the period, or more generally more than width - 2 digits before the period, your format with .0 will not work anymore.
String.format() uses half up as a rounding method, so you can use it as a first step to get your output to eight characters.
BigDecimal b = new BigDecimal(0.0045);
String format = "%8f";
String str = String.format(format, b);
Output:
12345.678900
123.000000
0.123457
1.500000
0.004500
12.000000
123456.000000
By default String.format() uses a precision of 6 for BigDecimal. To get your custom precision, you need to know how many digits are before the period and substract this number (+ 1 for the period itself) from the total width.
width - digits - 1
To get the number of digits you can simply check if (number % radix) == number applies for radix = 10, 100, 1000, ... As soon as it fits you know the number of digits.
public static int digitsBeforePeriod(BigDecimal b) {
int number = b.intValue();
int radix = 10;
int digits = 1;
while((number % radix) != number) {
radix *= 10;
++digits;
}
return digits;
}
So the next step is to modify the format:
BigDecimal b = new BigDecimal(0.0045);
int digits = digitsBeforePeriod(b);
String format = "%8." + (8 - digits - 1) + "f";
String str = String.format(format, b);
Output:
12345.68
123.0000
0.123457
1.500000
0.004500
12.00000
123456.0
Still there are lots of 0s, but at least the rounding is correct now. Now you specify that if a number is round to an integer, you want to print it with a .0 suffix, otherwise without.
To achieve this there might also exist clever formats, I didn't think any further though. The naive way to do this is simply:
while(str.endsWith("0") && !str.endsWith(".0")) {
str = str.substring(0, str.length()-1);
}
This removes the last character of the string until it either doesn't end on 0 at all or ends with .0.
Now the numbers will have the correct format, but are not aligned correctly:
12345.68
123.0
0.123457
1.5
0.0045
12.0
123456.0
To align them, just use the String.format() again.
str = String.format("%" + width + "s", str);
Output:
12345.68
123.0
0.123457
1.5
0.0045
12.0
123456.0
In the context this looks like the following. Note that I included a check wether or not the number can be formatted that way - if not, it will print Invalid. You can of course also just print the number, I just wanted to show the limitations of that format.
public static String trimToWidth(BigDecimal b, int width) {
String str = "Invalid";
int digits = digitsBeforePeriod(b);
// -2 for period and 0
if(digits <= width - 2) {
// use width and (width - digits - 1) as precision (-1 for period)
String format = "%" + width + "." + (width - digits - 1) + "f";
// rounds half-up
str = String.format(format, b);
// trim trailing 0s, unless it's .0
while(str.endsWith("0") && !str.endsWith(".0")) {
str = str.substring(0, str.length()-1);
}
}
// add spaces in front
str = String.format("%" + width + "s", str);
return str;
}
public static int digitsBeforePeriod(BigDecimal b) {
int number = b.intValue();
int radix = 10;
int d = 1;
while((number % radix) != number) {
radix *= 10;
++d;
}
return d;
}
public static void main(String[] args) {
double[] values = new double[] {
12345.6789, 123, 0.12345678,
1.5, 0.0045, 12, 123456, 1234567
};
BigDecimal[] bigs = new BigDecimal[values.length];
for(int i = 0; i < bigs.length; ++i) {
bigs[i] = new BigDecimal(values[i]);
System.out.println(trimToWidth(bigs[i], 8));
}
}

Related

Is there a faster way to convert a hexadecimal fractional part to a decimal one?

I wrote a program that generates digits of pi in hexadecimal. Every so often, at benchmark values, I would like to convert the hexadecimal value I have into a decimal one and save it to a file. Currently I am using BigDecimal to do that math with this code:
private static String toDecimal(String hex) {
String rawHex = hex.replace(".", "");
BigDecimal base = new BigDecimal(new BigInteger(rawHex, 16));
BigDecimal factor = new BigDecimal(BigInteger.valueOf(16).pow(rawHex.length() - 1));
BigDecimal value = base.divide(factor);
return value.toPlainString().substring(0, hex.length());
}
Note that this method will only work for hexadecimal values with one digit in the integral part, pi included, do not copy and paste this for general use.
So this code works fine, but for the latest benchmark, 2.5m digits, the conversion took 11.3 hours to complete.
Is there any faster way to do this manually?
I tried dividing the first decimal place by 16, the second by 16^2, etc but this would quickly get out of hand. Maybe some way of bitshifting the digits back to keep the divisor low? But potentially the n+1, n+2, n+3, etc digits need to be processed to get the correct value for n.
First, I believe your function toDecimal is wrong as it doesn't correctly convert input ".1a" (it's off by a factor of 16), for example, and throws an exception for input ".800". The third line should be:
BigDecimal factor = new BigDecimal(BigInteger.valueOf(16).pow(rawHex.length()));
The exception arises from:
return value.toPlainString().substring(0, hex.length());
The converted value could be shorter than the input value and you get a java.lang.StringIndexOutOfBoundsException.
Moving on:
In truth I have not benchmarked this against your current method; I just offer this as "food for thought." Here I am doing the multiplications as a child is taught to do it in school and in your case we have a large loop just to produce one digit. But if you could somehow adapt this to use BigDecimal (it's not clear how you would) it might be quicker than your current approach (what's really needed is a BigHexadecimal class).
It can be observed that converting a fraction from one base to another can be done using multiplications. In this case we have the following hexadecimal fraction (we can ignore the integer portion, which is 3 when converting pi):
.h1h2h3h4 ... hn
where hn is the nth hexadecimal "nibble".
We wish to convert the above to the following decimal fraction:
.d1d2d3d4 ... dn
where dn is the nth decimal digit.
If we were to multiply both quantities by 10, we would get:
h'1.h'2h'3h'4 ... h'n
The primes (`) denote that we have completely new hexadecimal nibble values following the multiplication.
and
d1.d2d3d4 ... dn
The multiplication by 10 just shifts the decimal fraction one place to the left.
We must note that the quantities to the left of the decimal point must be equal, i.e. d1 == h'1. Thus we repeatedly multiply our hexadecimal fraction by 10 and each time we do we take the integer portion as the next decimal digit for our conversion. We repeat this until either our new hexadecimal fraction becomes 0 or some arbitrary number of decimal digits have been produced:
See Java Demo
class Test {
private static String toDecimal(String hex, int numberDigits) {
/* converts a string such as "13.1a" in base 16 to "19.1015625" in base 10 */
int index = hex.indexOf('.');
assert index != -1;
StringBuilder decimal = new StringBuilder((index == 0) ? "" : String.valueOf(Integer.parseInt(hex.substring(0, index), 16)));
decimal.append('.');
int l = hex.length() - index - 1;
assert l >= 1;
int firstIndex = index + 1;
int hexDigits[] = new int[l];
for (int i = 0; i < l; i++) {
hexDigits[i] = Integer.parseInt(hex.substring(i + firstIndex, i + firstIndex + 1), 16);
}
while (numberDigits != 0 && l != 0) {
int carry = 0;
boolean allZeroes = true;
for (int i = l - 1; i >= 0; i--) {
int value = hexDigits[i] * 10 + carry;
if (value == 0 && allZeroes) {
l = i;
}
else {
allZeroes = false;
carry = (int)(value / 16);
hexDigits[i] = value % 16;
}
}
numberDigits--;
if (carry != 0 || (numberDigits != 0 && l != 0))
decimal.append("0123456789".charAt(carry));
}
return decimal.toString();
}
public static void main(String[] args) {
System.out.println(toDecimal("13.1a", 15));
System.out.println(toDecimal("13.8", 15));
System.out.println(toDecimal("13.1234", 15));
}
}
Prints:
19.1015625
19.5
19.07110595703125
Thank you to #Booboo for the solution to this problem. I improved on his code a little so it should work in every case. I wanted to post it here for future visitors.
/**
* Converts a hex number string to a decimal number string.
*
* #param hex The hex number string.
* #param accuracy The number of decimal places to return in the decimal number string.
* #return The decimal number string.
*/
public static String hexToDecimal(String hex, int accuracy) {
if (!hex.matches("[0-9A-Fa-f.\\-]+") || (accuracy < 0)) {
return "";
}
boolean negative = hex.startsWith("-");
hex = hex.replaceAll("^-", "");
String integral = hex.contains(".") ? hex.substring(0, hex.indexOf(".")) : hex;
String fraction = hex.contains(".") ? hex.substring(hex.indexOf(".") + 1) : "";
if (integral.contains("-") || fraction.contains(".") || fraction.contains("-")) {
return "";
}
StringBuilder decimal = new StringBuilder();
decimal.append(negative ? "-" : "");
decimal.append(integral.isEmpty() ? "0" : new BigDecimal(new BigInteger(integral, 16)).toPlainString());
if (fraction.isEmpty() || (accuracy == 0)) {
return decimal.toString();
}
decimal.append(".");
int numberDigits = accuracy;
int length = Math.min(fraction.length(), numberDigits);
int[] hexDigits = new int[numberDigits];
Arrays.fill(hexDigits, 0);
IntStream.range(0, length).boxed().parallel().forEach(i -> hexDigits[i] = Integer.parseInt(String.valueOf(fraction.charAt(i)), 16));
while ((numberDigits != 0)) {
int carry = 0;
for (int i = length - 1; i >= 0; i--) {
int value = hexDigits[i] * 10 + carry;
carry = value / 16;
hexDigits[i] = value % 16;
}
decimal.append(carry);
numberDigits--;
}
return decimal.toString();
}
/**
* Converts a hex number string to a decimal number string.
*
* #param hex The hex number string.
* #return The decimal number string.
* #see #hexToDecimal(String, int)
*/
public static String hexToDecimal(String hex) {
String fraction = hex.contains(".") ? hex.substring(hex.indexOf(".") + 1) : "";
return hexToDecimal(hex, fraction.length());
}
public static void main(String[] args) {
//integer
Assert.assertEquals("0", hexToDecimal("0"));
Assert.assertEquals("1", hexToDecimal("1"));
Assert.assertEquals("9", hexToDecimal("9"));
Assert.assertEquals("15", hexToDecimal("F"));
Assert.assertEquals("242", hexToDecimal("F2"));
Assert.assertEquals("33190", hexToDecimal("81A6"));
Assert.assertEquals("256", hexToDecimal("100"));
Assert.assertEquals("1048576", hexToDecimal("100000"));
Assert.assertEquals("5191557193152165532727847676938654", hexToDecimal("FFF6AA0322BC458D5D11A632099E"));
Assert.assertEquals("282886881332428154466487121231991859970997056152877088222", hexToDecimal("B897A12C89896321C454A7DD9E150233CBB87A9F0233DDE"));
Assert.assertEquals("-256", hexToDecimal("-100"));
Assert.assertEquals("-144147542", hexToDecimal("-8978456"));
Assert.assertEquals("-332651442596728389665499138728075237402", hexToDecimal("-FA42566214321CC67445D58EE874981A"));
Assert.assertEquals("33190", hexToDecimal("81a6"));
//decimal
Assert.assertEquals("0.10", hexToDecimal("0.1a"));
Assert.assertEquals("0.5", hexToDecimal("0.8"));
Assert.assertEquals("0.0711", hexToDecimal("0.1234"));
Assert.assertEquals("0.528966901", hexToDecimal("0.876A5FF4A"));
Assert.assertEquals("-0.528966901", hexToDecimal("-0.876A5FF4A"));
Assert.assertEquals("-0.00000000", hexToDecimal("-0.00000001"));
Assert.assertEquals("-0.62067648792835838863907521427468", hexToDecimal("-0.9EE4A7810C666FF7453D06A44621030E"));
Assert.assertEquals("0.528966901", hexToDecimal("0.876a5ff4a"));
Assert.assertEquals("0.528966901", hexToDecimal(".876a5ff4a"));
Assert.assertEquals("-0.528966901", hexToDecimal("-.876a5ff4a"));
//combined
Assert.assertEquals("15.33693", hexToDecimal("F.56412"));
Assert.assertEquals("17220744.33934412", hexToDecimal("106C488.56DF41A2"));
Assert.assertEquals("282886881332428154466487121231991859970997056152877088222.62067648792835838863907521427468", hexToDecimal("B897A12C89896321C454A7DD9E150233CBB87A9F0233DDE.9EE4A7810C666FF7453D06A44621030E"));
Assert.assertEquals("-17220744.33934412", hexToDecimal("-106C488.56DF41A2"));
Assert.assertEquals("-282886881332428154466487121231991859970997056152877088222.62067648792835838863907521427468", hexToDecimal("-B897A12C89896321C454A7DD9E150233CBB87A9F0233DDE.9EE4A7810C666FF7453D06A44621030E"));
Assert.assertEquals("-17220744.33934412", hexToDecimal("-106c488.56df41a2"));
Assert.assertEquals("3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808", hexToDecimal("3.243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01377BE5466CF34E90C6CC0AC29B7C97"));
//accuracy
Assert.assertEquals("-0.00000", hexToDecimal("-0.00000001", 5));
Assert.assertEquals("-0.000000000232830", hexToDecimal("-0.00000001", 15));
Assert.assertEquals("-0", hexToDecimal("-0.00000001", 0));
Assert.assertEquals("282886881332428154466487121231991859970997056152877088222.5", hexToDecimal("B897A12C89896321C454A7DD9E150233CBB87A9F0233DDE.9EE4A7810C666FF7453D06A44621030E", 1));
Assert.assertEquals("3.14", hexToDecimal("3.243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01377BE5466CF34E90C6CC0AC29B7C97", 2));
Assert.assertEquals("3.1415926535", hexToDecimal("3.243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01377BE5466CF34E90C6CC0AC29B7C97", 10));
Assert.assertEquals("3.1415926535897932384626433", hexToDecimal("3.243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01377BE5466CF34E90C6CC0AC29B7C97", 25));
//invalid
Assert.assertEquals("", hexToDecimal("0.00000.001"));
Assert.assertEquals("", hexToDecimal("0.00000-001"));
Assert.assertEquals("", hexToDecimal("156-081.00000001"));
Assert.assertEquals("", hexToDecimal("hello"));
Assert.assertEquals("", hexToDecimal("9g"));
Assert.assertEquals("", hexToDecimal("9G"));
Assert.assertEquals("", hexToDecimal("546.FDA", -1));
Assert.assertEquals("", hexToDecimal("546.FDA", -999));
}

Remove nth digit from an integer without converting to string

Say I have this integer in java, 987654321. I want to be able to remove, say the third and fourth digits, so I can get 9876521 in java.
I know I can do this by converting to a string, then taking a substring, but is there a way to do this without converting to a string?
% and / are your friends here! Using modulus and division we can get pieces of the number that we want.
We use modulus to eliminate the most significant digits, and division to eliminate the least significant digits. We can use division because the remainder gets truncated.
Then we put the two pieces we got from these two operations together. However, we need to shift the digits we got from the division to have room for the least significant digits.
Take 987654321 / 10000, this will give you 98765 (let's call this x)
Take 987654321 % 100, this will give you 21 (let's call this y)
x * 100 + y = 9876521.
More generally, if you want to remove a to bth digits from the number n (where a < b),
n % 10^(a-1) + ((n / 10^(b)) * 10^(a-1))
This will remove only one digit:
public static int RemoveNthPosition(int input, int position) {
int leftDivider = (int) Math.pow(10.0, position);
int rightDivider = (int) Math.pow(10.0, position - 1);
int leftSide = input / leftDivider;
int rightSide = input % rightDivider;
return leftSide * rightDivider + rightSide;
}
To remove multiple at the same time:
public static int RemoveMultiplePositions(int input, int[] positions) {
Arrays.sort(positions);
int result = input;
for (int count = 0; count < positions.length; count++) {
result = RemoveNthPosition(result, positions[count] - count);
}
return result;
}
In your case, it would be:
System.out.println(RemoveMultiplePositions(987654321, new int[] { 3, 4 }));

How do I force a number with unknown digits behind the decimal mark? [duplicate]

This question already has answers here:
How to merge two int(s) into a double in JAVA?
(6 answers)
Closed 6 years ago.
Is there a way to force a number to be placed behind the decimal mark of a number?
Say I have a number = 2, and another number = 23. Is there a way for me to force 23 into 0.23, so that when I add the numbers I end up with 2.23? And is there a way to do this when the number of digits in the second number, in this case 23, are unknown?
EDIT:
I realize this was badly written. I am currently working on a program that converts from imperial units to metric units. Part of the code looks like this:
double feet = nextDouble();
double inches = nextDouble();
double heightInMeters = (feet + (inches/10)) / 3.2808;
The problem with this code is that I anticipate that the user only enters a value <0, 9> for feet. Is there a way to force the input for inches to something like 0.x where x = inches so that it doesn't matter if the number is greater than 9?
Would be lovely if it was possible without using toString() and parseInt().
You can get the number of digits in an integer, i, using:
1 + Math.floor(Math.log10(i))
(not ceil(log10(i)), since that calculates that 1 has zero digits)
You then need to divide i by 10 to the power of that number:
i / Math.pow(10, 1 + Math.floor(Math.log10(i)))
e.g.
23 / Math.pow(10, 1 + Math.floor(Math.log10(23))) == 0.23
Ideone demo
Alternatively, if you think those floating point operations log and pow are too expensive, you can determine the number of digits with a loop:
int d = 1;
while (d < i) d *= 10;
then
System.out.println(i / (double) d);
(noting that you need to cast at least one of the numerator or denominator to a floating point type, otherwise it will use integer division).
Try parsing to double like this, from a string:
Option 1
try
{
int n = 2;
int decimal = 23;
String full = Integer.toString(n) + '.' + Integer.toString(decimal);
double val = Double.parseDouble(full);
} catch (Exception e) //Or whatever exception
{
//Code
}
Option 2
Of course, there are simpler methods, like this:
try
{
int n = 2;
int decimal = 23;
double val = Double.parseDouble(n + "." + decimal);
} catch (Exception e) //Or whatever exception
{
//Code
}
I personally would recommend the solution directly above, since it is the simplest, and requires the least code.
Live Example for Second Option
Simple implementation using Strings:
public class Mainclass {
public static void main(String[] args) {
Integer num = 1546;
Integer num2 = 2;
String s = num.toString();
s = "0." + s;
Double d = Double.parseDouble(s);
System.out.println(num2+d ); // 2.1546
}
}

How Can I Convert Very Large Decimal Numbers to Binary In Java

For instance, How would I be able to convert 2^60 or 12345678901234567890123456789012345678901234567890 to binary?
Basically, numbers that are too large to represent in Java.
Edit: I will be making a class that will be able to represent number that are too large. I'm just having a hard time figuring our how to convert decimal to binary.
Edit2: And also, I am not allowed to use BigDecimal, BigInteger, or any other library, sorry for not specifying earlier.
Here is a quik&dirty (very very very dirty) code:
public class BigDec2Bin {
public static int[] string2arrayReversed( String s )
{
char a[] = s.toCharArray();
int b[] = new int[ s.length() ];
for( int i = 0; i < a.length; i++ )
{
b[a.length-1-i] = a[i] - 48;
}
return b;
}
// adds two binary numbers represented as strings
public static String add( String s1, String s2 )
{
String result = "", stmp;
int[] a1, a2;
int ctmp, mark = 0;
// a1 should be the longer one
a1 = string2arrayReversed( ( s1.length() > s2.length() ? s1 : s2 ) );
a2 = string2arrayReversed( ( s1.length() < s2.length() ? s1 : s2 ) );
for( int i = 0; i < a1.length; i++ )
{
ctmp = a1[i] + ( i < a2.length ? a2[i] : 0 ) + mark;
switch( ctmp )
{
default:
case 0:
stmp = "0";
mark = 0;
break;
case 1:
stmp = "1";
mark = 0;
break;
case 2:
stmp = "0";
mark = 1;
break;
case 3:
stmp = "1";
mark = 1;
break;
}
result = stmp + result;
}
if( mark > 0 ) { result = "1" + result; }
return result;
}
public static String dec2bin( String s )
{
String result = "";
for( int i = 0; i < s.length() ; i++ )
{
result = add( result + "0", result + "000" );
result = add( result, Integer.toBinaryString( s.charAt(i) - 48 ) );
}
return result;
}
public static void main( String[] args )
{
String dec = "12345"; // should be 11000000111001
System.out.println( "dec2bin( " + dec + " ) = " + dec2bin( dec ) );
dec = "12345678901234567890123456789012345678901234567890";
System.out.println( "dec2bin( " + dec + " ) = " + dec2bin( dec ) );
}
}
Output:
dec2bin( 12345 ) = 011000000111001
dec2bin(
12345678901234567890123456789012345678901234567890
) =
10000111001001111111011000110110100110101010111110000011110010100001010100000010011001110100011110101111100011000111111100011001011011001110001111110000101011010010
My main idea is to use always strings.
add -method adds two binary numbers which are represented as strings
dec2bin -method is where the magic happens.
Allow me to explain:
result = add( result + "0", result + "000" );
is a calculation to multiply any given number by 10.
Multiplying a binary number by 10 is the same as adding the number with shifts:
x*10 <=> x<<1 + x<<3
result = add( result, Integer.toBinaryString( s.charAt(i) - 48 ) );
just adds a the next digit (from left to right) on the result string
Basicly what I'm doing is for example with 1234:
0*10 + 1 = 1
1*10 + 2 = 12
12*10 + 3 = 123
123*10 + 4 = 1234
but only in binary (represented as strings).
I hope i could help and sorry for my bad english.
Try this:
new BigDecimal("12345678901234567890123456789012345678901234567890").toString(2);
Edit:
For making a big-number class, you may want to have a look at my post about this a week ago. Ah, the question was by you, never mind.
The conversion between different number systems in principle is a repeated "division, remainder, multiply, add" operation. Let's look at an example:
We want to convert 123 from decimal to a base 3 number. What do we do?
Take the remainder modulo 3 - prepend this digit to the result.
Divide by 3.
If the number is bigger than 0, continue with this number at step 1
So it looks like this:
123 % 3 == 0. ==> The last digit is 0.
123 / 3 == 41.
41 % 3 == 2 ==> The second last digit is 2.
41 / 3 == 13
13 % 3 == 1 ==> The third digit is 1.
13 / 3 == 4
4 % 3 == 1 ==> The fourth digit is 1 again.
4 / 3 == 1
1 % 3 == 1 ==> The fifth digit is 1.
So, we have 11120 as the result.
The problem is that for this you need to have already some kind of division by 3 in decimal format, which is usually not the case if you don't implement your number in a decimal-based format (like I did in the answer to your last question linked above).
But it works for converting from your internal number format to any external format.
So, let's look at how we would do the inverse calculation, from 11120 (base 3) to its decimal equivalent. (Base 3 is here the placeholder for an arbitrary radix, Base 10 the placeholder for your internal radix.) In principle, this number can be written as this:
1 * 3^4 + 1 * 3^3 + 1*3^2 + 2*3^1 + 0*3^0
A better way (faster to calculate) is this:
((((1 * 3) + 1 )*3 + 1 )*3 + 2)*3 + 0
1
3
4
12
13
39
41
123
123
(This is known as Horner scheme, normally used for calculating values of polynomials.)
You can implement this in the number scheme you are implementing, if you know how to represent the input radix (and the digits) in your target system.
(I just added such a calculation to my DecimalBigInt class, but you may want to do the calculations directly in your internal data structure instead of creating a new object (or even two) of your BigNumber class for every decimal digit to be input.)
What about this approach:
result = 0;
for each digit in the decimal number, from left to right
result = result * 10 + digit;
return result;
So we need a way to represent an arbitrarily large binary number, and implement multiplication by 10 and addition of small numbers.
The most straightforward way to represent an arbitrarily large binary number is an array of its binary digits. You can then apply the algorithms for addition and multiplication your learned in elementary school, except that digits will "overflow" when they exceed 1 rather than 9. For example:
1010 * 1100111
----------------
11001110
+ 1100111000
----------------
10000000110
Pew: thanks, that works for some numbers. The number 6123456789012 however doesn't work, but here is the fix:
// a1 should be the longer one
a1 = string2arrayReversed( ( s1.length() >= s2.length() ? s1 : s2 ) ); //GREATER EQUAL
If you only work with integers, use BigInteger.toByteArray.
If not, unfortunately BigDecimal doesn't have that method. But I suppose you can always (in both cases) just ASCII encode the string representation of the number, if the binary form is just meant for transfer and not calculation anywhere.
there is a fast program to get the binary representation of a huge decimal.
This programm is indeed fast, it takes only 20ms to deal with a decimal with 3000digits, eg:string(3000,'2')+'12345'. because of the pursuit of efficiency, it is not very readable. you can modify it yourself to make it easier to understand.
inline string remove_pre_zero(const string& a)
{
auto t = a.find_first_not_of('\0', 0);
if (t == a.npos)
return string("0");
else
return a.substr(t);
}
string convert_to_bin(const string& _s)
{
const static string str[] = { "0", "1" };
string s(_s.size(), '0');
string binary;
binary.reserve(_s.size()*3);
int i = 0;
for (const auto& c : _s)
s[i++] = (c - '0');
while (s!="0")//simulate divide by 2
{
int t = 0, old_t = 0;
for (auto& ch : s)
{
t = ((old_t * 10 + ch) & 1);
ch = (ch + old_t * 10) >>1;
old_t = t;
}
binary += str[t];
if (s[0] == 0)
s = remove_pre_zero(s);
}
return string(binary.rbegin(), binary.rend());
}

Format Double as Fraction [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
Is there a library that will convert a Double to a String with the whole number, followed by a fraction?
For example
1.125 = 1 1/8
I am only looking for fractions to a 64th of an inch.
Your problem is pretty simple, because you're assured the denominator will always divide 64. in C# (someone feel free to translate a Java version):
string ToMixedFraction(decimal x)
{
int whole = (int) x;
int denominator = 64;
int numerator = (int)( (x - whole) * denominator );
if (numerator == 0)
{
return whole.ToString();
}
while ( numerator % 2 == 0 ) // simplify fraction
{
numerator /= 2;
denominator /=2;
}
return string.Format("{0} {1}/{2}", whole, numerator, denominator);
}
Bonus: Code Golf
public static string ToMixedFraction(decimal x) {
int w = (int)x,
n = (int)(x * 64) % 64,
a = n & -n;
return w + (n == 0 ? "" : " " + n / a + "/" + 64 / a);
}
One problem you might run into is that not all fractional values can be represented by doubles. Even some values that look simple, like 0.1. Now on with the pseudocode algorithm. You would probably be best off determining the number of 64ths of an inch, but dividing the decimal portion by 0.015625. After that, you can reduce your fraction to the lowest common denominator. However, since you state inches, you may not want to use the smallest common denominator, but rather only values for which inches are usually represented, 2,4,8,16,32,64.
One thing to point out however, is that since you are using inches, if the values are all proper fractions of an inch, with a denominator of 2,4,8,16,32,64 then the value should never contain floating point errors, because the denominator is always a power of 2. However if your dataset had a value of .1 inch in there, then you would start to run into problems.
How about org.apache.commons.math ? They have a Fraction class that takes a double.
http://commons.apache.org/math/api-1.2/org/apache/commons/math/fraction/Fraction.html
You should be able to extend it and give it functionality for the 64th. And you can also add a toString that will easily print out the whole number part of the fraction for you.
Fraction(double value, int
maxDenominator) Create a fraction
given the double value and maximum
denominator.
I don't necessarily agree, base on the fact that Milhous wants to cover inches up to 1/64"
Suppose that the program demands 1/64" precision at all times, that should take up 6 bits of the mantissa. In a float, there's 24-6 = 18, which (if my math is right), should mean that he's got a range of +/- 262144 + 63/64"
That might be enough precision in the float to convert properly into the faction without loss.
And since most people working on inches uses denominator of powers of 2, it should be fine.
But back to the original question, I don't know any libraries that would do that.
Function for this in a C-variant called LPC follows. Some notes:
Addition to input value at beginning is to try to cope with precision issues that otherwise love to wind up telling you that 5 is 4 999999/1000000.
The to_int() function truncates to integer.
Language has a to_string() that will turn some floats into exponential notation.
string strfrac(float frac) {
int main = to_int(frac + frac / 1000000.0);
string out = to_string(main);
float rem = frac - to_float(main);
string rep;
if(rem > 0 && (to_int(rep = to_string(rem)) || member(rep, 'e') == Null)) {
int array primes = ({ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 });
string base;
int exp;
int num;
int div;
if(sscanf(rep, "%se%d", base, exp) == 2) {
num = to_int(replace(base, ".", ""));
div = to_int(pow(10, abs(exp)));
} else {
rep = rep[2..];
num = to_int(rep);
div = to_int(pow(10, strlen(rep)));
}
foreach(int prime : primes) {
if(prime > num)
break;
while((num / prime) * prime == num && (div / prime) * prime == div) {
num /= prime;
div /= prime;
}
}
out += " " + num + "/" + div;
}
return out;
}
i wrote this for my project i hope it could be usefull:
//How to "Convert" double to fraction("a/b") - kevinlopez#unitec.edu
private boolean isInt(double number){
if(number%2==0 ||(number+1)%2==0){
return true;
}
return false;
}
private String doubleToFraction(double doub){
//we get the whole part
int whole = (int)doub;
//we get the rest
double rest = doub - (double)whole;
int numerator=1,denominator=1;
//if the whole part of the number is greater than 0
//we'll try to transform the rest of the number to an Integer
//by multiplying the number until it become an integer
if(whole >=1){
for(int i = 2; ; i++){
/*when we find the "Integer" number(it'll be the numerator)
* we also found the denominator(i,which is the number that transforms the number to integer)
* For example if we have the number = 2.5 when it is multiplied by 2
* now it's 5 and it's integer, now we have the numerator(the number (2.5)*i(2) = 5)
* and the denominator i = 2
*/
if(isInt(rest*(double)i)){
numerator = (int)(rest*(double)i);
denominator = i;
break;
}
if(i>10000){
//if i is greater than 10000 it's posible that the number is irrational
//and it can't be represented as a fractional number
return doub+"";
}
}
//if we have the number 3.5 the whole part is 3 then we have the rest represented in fraction 0.5 = 1/2
//so we have a mixed fraction 3+1/2 = 7/2
numerator = (whole*denominator)+numerator;
}else{
//If not we'll try to transform the original number to an integer
//with the same process
for(int i = 2; ; i++){
if(isInt(doub*(double)i)){
numerator = (int)(doub*(double)i);
denominator = i;
break;
}
if(i>10000){
return doub+"";
}
}
}
return numerator+"/"+denominator;
}
My code looks like this.
public static int gcd(int a, int b)
{
if (b == 0)
return a;
else
return gcd(b, a % b);
}
public static String doubleToStringFraction(Double d)
{
StringBuffer result = new StringBuffer(" " + ((int) Math.floor(d)));
int whole = (int) ((d - Math.floor(d)) * 10000);
int gcd = gcd(whole, 10000);
result.append(" " + (whole / gcd) + "/" + 10000 / gcd + " ");
return result.toString();
}
As several others have poited out, fractions of 64 can be precicely represented by IEEE-floats. This means we can also convert to a fraction by moving and masking bits.
This is not the place to explain all details of floating point representations, please refer to wikipedia for details.
Briefly: a floating point number is stored as (sign)(exp)(frac) where sign is 1 bit, exp is 11 bits and frac is the fraction part (after 1.) and is 52 bits. This is enterpreted as the number:
(sign == 1 ? -1 : 1) * 1.(frac) * 2^(exp-1023)
Thus, we can get the 64th by moving the point accoring to the exponent and masking out the 6 bits after the point. In Java:
private static final long MANTISSA_FRAC_BITMAP = 0xfffffffffffffl;
private static final long MANTISSA_IMPLICIT_PREFIX = 0x10000000000000l;
private static final long DENOM_BITMAP = 0x3f; // 1/64
private static final long DENOM_LEN = 6;
private static final int FRAC_LEN = 52;
public String floatAsFrac64(double d) {
long bitmap = Double.doubleToLongBits(d);
long mantissa = bitmap & MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX;
long exponent = ((bitmap >> FRAC_LEN) & 0x7ff) - 1023;
boolean negative = (bitmap & (1l << 63)) > 0;
// algorithm:
// d is stored as SE(11)F(52), implicit "1." before F
// move point to the right <exponent> bits to the right:
if(exponent > FRAC_LEN) System.out.println("warning: loosing precision, too high exponent");
int pointPlace = FRAC_LEN-(int)exponent;
// get the whole part as the number left of the point:
long whole = mantissa >> pointPlace;
// get the frac part as the 6 first bits right of the point:
long frac = (mantissa >> (pointPlace-DENOM_LEN)) & DENOM_BITMAP;
// if the last operation shifted 1s out to the right, we lost precision, check with
// if any of these bits are set:
if((mantissa & ((MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX) >> (pointPlace - DENOM_LEN))) > 0) {
System.out.println("warning: precision of input is smaller than 1/64");
}
if(frac == 0) return String.format("%d", whole);
int denom = 64;
// test last bit, divide nom and demon by 1 if not 1
while((frac & 1) == 0) {
frac = frac >> 1;
denom = denom >> 1;
}
return String.format("%d %d/%d", whole, frac, denom);
}
(this code can probably be made shorter, but reading bit-flipping-code like this is hard enough as it is...)
I create simply Fraction library.
The library is available here: https://github.com/adamjak/Fractions
Example:
String s = "1.125";
Fraction f1 = Fraction.tryParse(s);
f1.toString(); // return 9/8
Double d = 2.58;
Fraction f2 = Fraction.createFraction(d);
f2.divide(f1).toString() // return 172/75 (2.29)
To solve this problem (in one of my projects), I took the following steps:
Built a dictionary of decimal/fraction strings.
Wrote a function to search the dictionary for the closest matching fraction depending on the "decimal" part of the number and the matching criteria.

Categories

Resources