I am trying to make a program, that displays rather large numbers (BigInteger large). To make it easier for the user, I am displaying the numbers as string (1 000 000 = 1 Million). My below code shows my current attempt. My problem lies, in the fact that the actual number that I am replacing will not be nice and round. This program does not need to display the 100% accurate value, rather give a ball park figure. I.E:
1 234 567 = 1 Million
1 000 000 = 1 Million
1 934 234 = 1 Million
My current code (Shortened for brevity):
if (!displayNumbers) {
StringBuffer sb = new StringBuffer(_combinations);
String t = sb.reverse().toString();
Toast.makeText(getApplicationContext(), t, 1).show();
...
if (t.contains("000 000 000 000 000")) {
t.replace("000 000 000 000 000", "quadrillion");
}
if (t.contains("000 000 000 000")) {
t.replace("000 000 000 000", "trillion");
}
if (t.contains("000 000 000")) {
t.replace("000 000 000", "billion");
}
if (t.contains("000 000")) {
t.replace("000 000", "million");
}
sb = new StringBuffer(t);
_combinations = sb.reverse().toString();
}
I was thinking something along the lines of replacing the zero's with #'s so that it would just search for x lots of 3 digits and replace with corresponding word, but I do not know how to implement this. I should also note that I am aware that the million, billion, etc are currently spelt backwards in the final output string.
EDIT: Should note for UK readers that I am using USA definitions of *illions.
EDIT 2: Been doing some more Googling and found this - java Regex: replace all numerical values with one number. Except I have no idea how to modify the Regex for finding 000 000.
you can do that using if and else statements, from what I understand you're not looking for exact representation , so it is ok to say 1 Million for 1400000.
To do so you can use code like this:
int myValue
String txt = null;
if (myValue> 500 000 000){
txt = String.ValueOf((int) myValue/1000000000) + " Billion"
} else if (myValue> 500 000){
txt = String.ValueOf((int) myValue/1000000) + " Million"
} else if (myValue> 500) {
txt = String.ValueOf((int) myValue/1000) + " Thousand"
}
This should work for the simple usage you're describing.
Your number(in text) is just for display purpose. It's bad idea to manipulate numbers after converting them to string.
Why don't you just parse the text to integer and format it with a proper way for display.
Here is a example you should take a look
I would like to thank everyone else for their help with suggestions on how to fix this, and I am sure that they are all possible. However, I found that the best way, was to still convert the BigInteger to string, but not to add whitespace.
If the string's length was less than 6 I left it as it was and simply added whitespace for looks. If it was longer then 6, I took the first 1, 2 or 3 numbers depending on the Mod of the string's length and used them as actual numbers to show at the start (1 Million, 34 Billion, 124 Quadrillion, etc).
This was then followed by seeing how long the remaining string was, and simply adding more string to the display text if it was longer then a certain amount.
If anyone needs clarification of this, please do not hesitate to ask me! Here is the link to the pastebin - http://pastebin.com/3X681UkG
Good luck!
Example implementation that rounds numbers over one thousand to one significant figure.
Optionally, groups of three digits may be separated by comma or space.
private final static String[] illions = {
"m", "b", "tr", "quadr", "quint", "sext", "sept", "oct", "non", "dec",
"undec", "duodec", "tredec", "quattuordec", "quindec", "sexdec",
"septendec", "octodec", "novemdec", "vigint", "unvigint", "duovigint",
"trevigint", "quattuorvigint", "quinvigint", "sexvigint", "septenvigint",
"octovigint", "novemvigint", "trigint", "untrigint", "duotrigint"
};
private static String approximate( String n ) {
String approx = n;
if ( n != null && n.matches( "^\\d{1,3}[\\s,]?(\\d{3}[\\s,]?)*\\d{3}$" ) ) {
n = n.replaceAll( "[\\s,]", "" );
int i = n.length() + 2;
if ( i < 105 ) {
int rnd = (int) Math.round( Double.parseDouble( n.substring( 0, 2 ) ) / 10 )
* (int) Math.pow(10, i % 3);
n = i > 8 ? illions[ i / 3 - 3 ] + "illion" : "thousand";
approx = rnd + " " + n;
}
}
return approx;
}
Test
System.out.printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
approximate("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"), // "1000 duotrigintillion"
approximate("4857476486598746598743265987426598724365987265987324598726598734625987564987456"), // "5 quinvigintillion"
approximate("56843584275874587243582837465847326554"), // "60 undecillion"
approximate("1 345 678 910 111 213"), // "1 quadrillion"
approximate("47,648,658,437,651"), // "50 trillion"
approximate("9 891 011 123"), // "10 billion"
approximate("687654321"), // "700 million"
approximate("32 456 999"), // "30 million"
approximate("2,678,910"), // "3 million"
approximate("1 234 567"), // "1 million"
approximate("123456"), // "100 thousand"
approximate("17,654"), // "20 thousand"
approximate("8765"), // "9 thousand"
approximate("654") // "654"
);
I would just simply count digits. rough code from scratch:
String result = "";
Pattern pattern = Pattern.compile("[\\s0-9]+");
Matcher matcher = pattern.matcher(t);
int index = 0;
while (matcher.find()) {
int newIndex = matcher.start();
result += t.substring(index, newIndex);
result += convert(matcher.group());
index = matcher.end() + 1;
}
result += t.substring(index, t.length() - 1);
private String convert(String uglyNumber) {
// get rid of whitespaces
String number = uglyNumber.replace("\\s", "");
// simply count digits
if( 6 < number.length() && number.length() <= 9 ) {
return "million";
} else if( 9 < number.length() && number.length() <= 12 ) {
return "million";
} else ...
return ulgyNumber;
}
if numbers are more compilcated than simple mix of digits and whitespaces, you may want to have a look for this regex site:
Related
how can i only have the first 5 numbers in a double?
12345.6789 --> 12345
1.23456789 --> 1.2345
0.123456789 --> 0.1234
I have tried different things such as multiplying the number by the power of 10 and x
but x seems to only affect the decimal places, setting it to x decimal places
Using BigDecimal:
move the decimal point to the right/left until only the desired number of (significant) digits are in front of the decimal point;
set the scale to zero, that is, removed all digits after the decimal point or extend the precision up to the point (if missing digits);
move the decimal point back to where it was, undoing first step.
static BigDecimal convert(BigDecimal number, int significant) {
var beforeDecimal = number.precision() - number.scale();
var movePoint = significant - beforeDecimal;
return number
.movePointRight(movePoint)
.setScale(0, RoundingMode.DOWN)
.movePointLeft(movePoint);
}
called like
convert(BigDecimal.valueOf(1.23456789), 5)
or
convert(new BigDecimal("1.23456789"), 5)
to return the BigDecimal:
1.2345
Here is one way.
convert to a string.
check for a decimal.
if not present (= -1) or >= 5, set i to 5. Else set to 6
then get the substring from 0 to i.
double[] v = {12345.6789,
1.23456789,
0.123456789};
for (double d : v) {
String s = Double.toString(d);
int i = s.indexOf('.');
i = i < 0 || i >= 5 ? 5 : 6;
System.out.println(s.substring(0,i));
}
prints
12345
1.2345
0.1234
As others commented, one way to do this is with string manipulation. May not seem elegant, but it gets the job done.
Define some inputs.
double[] doubles = { 12345.6789 , 1.23456789 , 0.123456789 } ;
Loop those inputs. For each one, generate a string. If a FULL STOP is found, take first six characters. If not, five. If the FULL STOP lands in the last place, such as in our first example input, delete.
for( double d : doubles )
{
String s = String.valueOf( d ) ;
String s5 = s.substring( 0 , s.contains( "." ) ? 6 : 5 ) ;
if( s5.endsWith( "." ) ) { s5 = s5.substring( 0 , s5.length() - 1 ) ; } // Drop FULL STOP in last place.
System.out.println( s5 ) ;
}
See this code run live at IdeOne.com.
12345
1.2345
0.1234
Caveat: You may need to internationalize by accounting for a COMMA (,) as the decimal separator rather than a FULL STOP (.) character.
String s5 = s.substring( 0 , ( s.contains( "." ) || s.contains( "," ) ) ? 6 : 5 ) ;
i ended up going with the following:
//cuts the string to the first 6 letters
String sixLetter = aDouble.toString().substring(0,6);
//checks if there is a "." before the 5th character so we don't end up with 12345.
if (sixLetter.indexOf(".") > 4) {
//then assuming there is not a "." it will cut it to the first 5 characters
return sixLetter.substring(0,5);
//if there is a "." (other than 12345.) it will cut it down to 5 characters resulting in 12345
};
return sixLetter;
I'm struggling with this algorithm. It should work like this:
If I input f.e. 6880, my program should output 80 86 80 86 60 68 68.
As you can see, combinations are repeating. That's because it looks at every digit as it is a different object. In my program it's correct.
Here is my code:
public static Set<Integer> get2DCombinations(List<Integer> digits) {
Set<Integer> combinations = new TreeSet<>();
int t = 0;
for(Integer i : digits) {
for (Integer j : digits) {
t = i * 10 + j;
if (t / 10 >= 1) {
combinations.add(t);
}
}
}
return combinations;
}
It returns a specific set of combinations where all combinations have 2 digits.
It works perfectly, but only with 4-digit numbers. Of course, I can use one more for-each loops, but is there a way to automate it?
So if I input 6-digit number it should output all possible 3-digit combinations of its digits, and if I input 8-digit number, it should output all possible 4-digit combinations. Input numbers always have even amount of digits.
Could you please point out for me how to do so?
You need a recursive program that will generate all the combinations for your input. Here's a solution of mine. My method accepts a String as input (it's just shorted program and easier, you can adapt it to your needs):
public static Set<String> get2DCombinations(String input) {
return backtracking("", input, input.length() / 2) ;
}
public static Set<String> backtracking(String actual, String remaining, int length) {
if (actual.length() == length) {
return new HashSet<>(Arrays.asList(actual));
}
Set<String> result = new HashSet<>();
for(int i = 0; i < remaining.length(); i++) {
result.addAll(backtracking(actual + remaining.charAt(i), remaining.substring(0, i) + remaining.substring(i + 1), length));
}
return result;
}
And you call the method like so:
System.out.println(get2DCombinations(input));
Result:
[88, 68, 06, 80, 08, 60, 86]
As I mentioned in the comment, your are missing some of the combinations. This solution generates all of them.
Try calculating n / 2 first. So, if n is 6, then n / 2 = 3. Then you know before you start fining the combinations that you are looking for combinations of 3 digits.
Then you want to find the right algorithm to find the combinations. Part of problem solving is breaking down problems to smaller problems. That is what I did here.
I can not solve it for you, however, because it is better for you to solve yourself, and second, there details that you dind't provide so it is hard to give the right solution.
Given this numbers:
150.00
150.26
I need to round like this:
If the second value of decimal part is zero (0), then the new value should be 150.0
If the second value of decimal part is different than zero (0), then the new value keeps both decimals Ex.: 150.26
Is there a rounding method that can do this?
Thanks.
My code here:
String monto = "150.10";//150.26
String nuevoMonto = "";
String[] valores = monto.split("\\.");
System.out.println("Valores : " + valores);
System.out.println("Valores length: " + valores.length);
for (int i = 0; i < valores.length; i++) {
System.out.println("-->Valor: " + valores[i]);
}
if (valores.length == 2) {
nuevoMonto = valores[1];
System.out.println("Nuevomonto: " + nuevoMonto);
if (nuevoMonto.length() == 2) {
System.out.println("Dos posiciones decimales");
System.out.println(nuevoMonto.indexOf("0"));
if (nuevoMonto.indexOf("0") == 1) {
nuevoMonto = valores[0] + "." + nuevoMonto.substring(0, 1);
}
}
}
System.out.println("Nuevo monto: " + nuevoMonto);
try something like this:
DecimalFormat decimalFormat = new DecimalFormat("#.0#");
System.out.println(decimalFormat.format(150.09));
System.out.println(decimalFormat.format(150.10));
The string inside the new decimal format allows #s to be trimmed, and 0s are forced to stay
If you have a string to start with, perhaps there is no point parsing it to a floating point number and then reformatting it. Another approach is to use a regexp for this:
String monto = "150.10";
String nuevoMonto = monto.replaceAll("(?<=\\.\\d)0$", "")
The regexp consist of two parts. The first part (?<=\\.\\d) says the match need to be preceded by a dot and a digit. The second part 0$ matches the trailing zero at the end of the string. We replace it with nothing.
There is an expression in Java called floor method which can be used to always bring a value with any form of rounding to an integer value with any relevant decimal points removed from the data
a simple check for this wold be to take the data and check if the data is not modulo or mod of a zero
sample code might look like this.
if ((monto % 0.1 ) == 0)
{
//has no relevant data so it should be floored
Nuevomonto = floor (monto);
}
else
{
Nuevomonto = monto;
}
hope that helps.
I have several thousands of rows that I'm loading into a database utilizing Pentaho. I must keep the string value length at 4 characters. The source file may only have a single character but it is still needed to keep the length at 4 characters to maintain consistency in the database.
Example:
Source: 10
Database expected results: 0010
I'm using a replace in string transformation or could use a java expression, which ever one works. Please help and provide a resolution utilizing either method (Regex or Javascript expression).
Thanks,
In Java you can use String.format(...) with a format specifier to pad your number with zeroes to 4 digits:
String.format("%04d", yournumber);
In Javascript you can use sprintf(...) for the same task:
var s = sprintf("%04d", yournumber);
So apparently sprintf() isn't standard Javascript but is a library you can download if you like. You can always to do this to get your 4 digits though:
// take the last 4 digits from the right
var s = String("0000" + yournumber).slice(-4);
And you could actually turn this into a simple left-padding function:
String.prototype.leftPad = function(paddingValue, paddingLength) {
return (new Array(paddingLength + 1).join(paddingValue) + this).slice(-paddingLength);
};
var s = String(yournumber).leftPad("0", 4)
(If you mean Javascript):
var str = "10";
function padIt(s) {
s = ""+s;
while (s.length < 4) {
s = "0" + s;
}
return s;
}
console.log(padIt(str));
http://jsfiddle.net/EzqRM/1/
For arbitrary padding of numbers, in javascript:
// padLeft(
// number to pad
// length to pad to
// character to pad with[optional, uses 0's if not specified]
// )
function padLeft(num, pad, padchr) {
num = "" + num;
pad = (pad || num.length) + 1;
return (num.length < pad ? new Array(pad - num.length).join(padchr || "0") : "") + num;
}
// returns "0010"
var input = 10
padded = padLeft(input, 4)
I have a requirement to shorten a 6 character string like "ABC123" into a unique 4 character string. It has to be repeatable so that the input string will always generate the same output string. Does anyone have any ideals how to do this?
It is not possible to do a fully unique mapping from a 6 character string to a 4 character string. This is an example of a simple hash function. Because the range space is smaller than the domain space, you are necessarily going to have some hash collisions. You can try to minimize the number of collisions based on the type of data you're going to be accepting, but ultimately it's impossible to map every 6 character string to a unique 4 character string, you would run out of 4 character strings.
You need some restrictions on the input string, otherwise math will inevitably bite you.
For example, let's assume you know that it consists of upper case letters and digits only. Therefore, there are 36^6 possible input strings.
The result needs to have less restrictions, e.g. you allow 216 different characters (printable extended ascii or something like that).
By pure coincidence, 216^4 = 36^6, so what you need is a mapping. That's easy, just use the algorithm for converting number representations from one radix to another.
Not sure this can be done, as I would bet there are some business constraints (like a user has to be able to type in the key).
The idea is to "hash" down the value into a smaller number of places. This requires a character set large enough to handle all combinations.
Let's assume the original key is case insensitive, you have 26 + 10 = 32, then raised to the 6th unique combinations (2,176,782,336 unique combinations). To match this in only 4 characters, you have to use a character set with 216 unique characters, as 216 ^ 6 is 2,176,782,336 or the first number raise to 4 with more combinations than a case insensitive key with numbers. (case insentivity, plus numerics only takes you to 62).
If we take the standard US keyboard, we have 26 letters x 2 cases = 52
10 numbers
10 special characters on number keys
11 other special character keys * 2 = 22
This is 94 unique characters, or less than half the uniques you need just to get a case insensitive 6 digit code into 4 digits. Now, on the Planet Klingon, where keyboards are much larger ... ;-)
If the key is case insensitive, your character set has to expand to 489 unique characters to fit in a 4 digit "hash". Ouch!
Assumption: The input string can only have characters with ASCII decimal values below 128... otherwise, as others have stated, this wont work.
public class Foo {
public static int crunch(String str) {
int y = 0;
int limit = str.length() > 6 ? 6 : str.length();
for (int i = 0; i < limit; ++i) {
y += str.charAt(i) * (limit - i);
}
return y;
}
public static void main(String[] args) {
String[] words = new String[]{
"abcdef", "acdefb", "fedcba", "}}}}}}", "ZZZZZZ", "123", "!"
};
for (int idx = 0; idx < words.length; ++idx) {
System.out.printf("in=%-6s out=%04d\n",
words[idx], crunch(words[idx]));
}
}
}
Generates:
in=abcdef out=2072
in=acdefb out=2082
in=fedcba out=2107
in=}}}}}} out=2625
in=ZZZZZZ out=1890
in= 123 out=0298
in= ! out=0033
You have to make assumptions about the range of values the characters can have and when is an acceptable encoded character. There are any number of ways you can do this. You could pack the String to 1,2,3,4 or 5 characters depending on your assumptions.
One simple example which would give you 4 characters is to assume the last three letters are a number.
public static String pack(String text) {
return text.substring(0, 3) + (char) Integer.parseInt(text.substring(3));
}
public static String unpack(String text) {
return text.substring(0, 3) + ("" + (1000 + text.charAt(3))).substring(1);
}
public static void main(String[] args) throws IOException {
String text = "ABC123";
String packed = pack(text);
System.out.println("packed length= " + packed.length());
String unpacked = unpack(packed);
System.out.println("unpacked= '" + unpacked + '\'');
}
prints
packed length= 4
unpacked= 'ABC123'