I am trying to get a desired output based on a variable input. I can get close to what I want but there seems to be an issue with rounding a number.
What I want by example (input > output).
30 > 30
30.0 > 30
30.5 > 30,5
30.5555 > 30,6
30.04 > 30
The problem is that the last one comes back as 30.0. Now I understand why this is happening (because of the rounding up/down)
My code:
private String getDistanceString(double distance) {
distance = 30.55;
DecimalFormat df = new DecimalFormat(".#");
if (distance == Math.floor(distance)) {
//If value after the decimal point is 0 change the formatting
df = new DecimalFormat("#");
}
return (df.format(distance) + " km").replace(".", ",");
}
It is almost always wrong to use == with floating point numbers. You should use Math.abs(a - b) < x.
private String getDistanceString(double distance) {
DecimalFormat df = new DecimalFormat(".#");
if (Math.abs(distance - Math.round(distance)) < 0.1d) {
//If value after the decimal point is 0 change the formatting
df = new DecimalFormat("#");
}
return (df.format(distance) + " km").replace(".", ",");
}
public void test() {
double[] test = {30d, 30.0d, 30.5d, 30.5555d, 30.04d, 1d / 3d};
for (double d : test) {
System.out.println("getDistanceString(" + d + ") = " + getDistanceString(d));
}
}
A hack around it, is to replace with regex
return
(""+df.format(distance))
.replaceAll("\\.(0+$)?", ",") //replace . and trailing 0 with comma,
.replaceAll(",$","") //if comma is last char, delete it
+ " km"; //and km to the string
Related
This question already has answers here:
Int division: Why is the result of 1/3 == 0?
(19 answers)
Closed 2 years ago.
I'd like to format following numbers into the numbers next to them with Android:
I've tried it, by taking the code from How to go about formatting 1200 to 1.2k in Android studio
This works when the value is a multiple of zero, but if there is a number other than zero some are not suitable
String numberString = "";
if (Math.abs(Integer.parseInt(weight_total) / 1000) > 1) {
numberString = (Integer.parseInt(weight_total) / 1000) + " kg";
}
else {
numberString = weight_total + " gram";
}
tvWeight.setText(": " + numberString);
I want to 1000 gram > 1 kg, 1800 gram > 1.8 kg etc
Correct and Wrong weight screenshot https://i.stack.imgur.com/mqA0x.jpg
Now i am using this code, so its work fine and perfect for my app
// Input data
int weightInput = Integer.parseInt(item.getWeight());
String weightOutput;
if (weightInput < 1000) {
weightOutput = weightInput + " gram";
} else {
double result = weightInput / 1000.0;
weightOutput = String.valueOf(result);
weightOutput = (weightOutput.contains(".0") ? weightOutput.substring(0, weightOutput.length() - 2) : weightOutput) + " kg";
}
System.out.println(weightOutput);
Final result https://i.stack.imgur.com/1Nxbs.png
Integer.parseInt convert String into int.
if you divide an int by int the result will be int too.
use Float.parseFloat() instead, or divide it by 1000.0 (make the 1000 non-integer value and then the result will be non-integer too.)
if you want it to be more precise and count milligrams (or even smaller units) you must use Float.parseFloat() because the value after the dot will be discarded using Integer.parseInt().
Here you go, I wrote a java code for this and I am sure it is going to work in android as well.
package stackOverflow;
import java.util.*;
import java.io.*;
public class parseWeight {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//Ignore above
//assuming you have int as input
int weightInput = Integer.parseInt(br.readLine());
String outputString = "";
if(weightInput < 1000){
outputString = weightInput + " gram";
}
else if(weightInput >= 1000){
double temp = weightInput / 1000.0;
//round off upto one decimal places
double rf = Math.round((temp*10.0)/10.0);
outputString = rf + " kg";
}
tvWeight.setText(": " + outputString);
//Ignore below
System.out.println(outputString);
}
}
What's the simplest way to display a double as String but not showing the decimal part if the double value is a whole number?
For example, If I have the double value 10.0 it would display only 10 (no decimal part), but if I have the double value 10.35 it would display the complete value 10.35.
I would use this approach:
// 1. Make sure to have dot instead of comma as a separator:
DecimalFormatSymbols symbol = new DecimalFormatSymbols(Locale.US);
// 2. Define max number of decimal places:
DecimalFormat df = new DecimalFormat("#.###", symbol);
// 3. Use it:
System.out.println(df.format(yourNumber));
Just use java.text.NumberFormat
Example:
final NumberFormat numberFormat = NumberFormat.getInstance();
System.out.println(numberFormat.format( 10.0d ));
System.out.println(numberFormat.format( 10.35d ));
the output will be:
10
10,35
double one = 1.00;
String stringNumber;
if (one % 1 == 0) {
Integer intOne = (int) one;
stringNumber = intOne.toString();
} else {
stringNumber = String.valueOf(one);
}
This works fine:
System.out.println(getString(4.53)); // 4.53
System.out.println(getString(4.0)); // 4
public String getString(double d){
return (d % 1 == 0) ? String.valueOf((int) d) : String.valueOf(d);
}
My app lets users search a location and one of the queries I got was
"78°14'09"N 15°29'29"E"
Obviously the user wants to go to this location.
First how do I check if this string fits the decimal format correctly. Then how do I convert it to double format?
double latitude = convertToDouble("78°14'09"N")
I searched here on stackoverflow but they are all looking for the opposite: double to decimal.
78°14'09"N 15°29'29"E
First how do I check if this string fits the decimal format correctly. Then how do I convert it to double format?
The string is not in decimal (degrees) format. It is in degrees, minutes, and seconds, which is more or less the opposite of decimal degrees format. I therefore interpret you to mean that you want to test whether the string is in valid D/M/S format, and if so, to convert it to decimal degrees, represented as a pair of doubles.
This is mostly a parsing problem, and regular expressions are often useful for simple parsing problems such as this one. A suitable regular expression can both check the format and capture the numeric parts that you need to extract. Here is one way to create such a pattern:
private final static Pattern DMS_PATTERN = Pattern.compile(
"(-?)([0-9]{1,2})°([0-5]?[0-9])'([0-5]?[0-9])\"([NS])\\s*" +
"(-?)([0-1]?[0-9]{1,2})°([0-5]?[0-9])'([0-5]?[0-9])\"([EW])");
That's a bit dense, I acknowledge. If you are not familiar with regular expressions then this is no place for a complete explanation; the API docs for Pattern provide an overview, and you can find tutorials in many places. If you find that your input matches this pattern, then not only have you verified the format, but you have also parsed out the correct pieces for the conversion to decimal degrees.
The basic formula is decimal = degrees + minutes / 60 + seconds / 3600. You have the additional complication that coordinates' direction from the equator / prime meridian might be expressed either via N/S, E/W or by signed N, E, or by a combination of both. The above pattern accommodates all of those alternatives.
Putting it all together, you might do something like this:
private double toDouble(Matcher m, int offset) {
int sign = "".equals(m.group(1 + offset)) ? 1 : -1;
double degrees = Double.parseDouble(m.group(2 + offset));
double minutes = Double.parseDouble(m.group(3 + offset));
double seconds = Double.parseDouble(m.group(4 + offset));
int direction = "NE".contains(m.group(5 + offset)) ? 1 : -1;
return sign * direction * (degrees + minutes / 60 + seconds / 3600);
}
public double[] convert(String dms) {
Matcher m = DMS_PATTERN.matcher(dms.trim());
if (m.matches()) {
double latitude = toDouble(m, 0);
double longitude = toDouble(m, 5);
if ((Math.abs(latitude) > 90) || (Math.abs(longitude) > 180)) {
throw new NumberFormatException("Invalid latitude or longitude");
}
return new double[] { latitude, longitude };
} else {
throw new NumberFormatException(
"Malformed degrees/minutes/seconds/direction coordinates");
}
}
The convert() method is the main one; it returns the coordinates as an array of two doubles, representing the coordinates in decimal degrees north and east of the intersection of the equator with the prime meridian. Latitudes south of the equator are represented as negative, as are longitudes west of the prime meridian. A NumberFormatException is thrown if the input does not match the pattern, or if the latitude or longitude apparently represented is invalid (the magnitude of the longitude cannot exceed 180°; that of the latitude cannot exceed 90°).
You won't be able to parse that into a double without removing the non number chars but,
String string = "78°14'09"N";
Double number = 0;
try{
number = Double.parseDouble(string);
//do something..
}catch (NumberFormatException e){
//do something.. can't be parsed
}
If you first remove any characters from the string that are not alphanumeric, then something along these lines will work. This code compiles.
public class HelloWorld{
public static void main(String []args){
String input = "78 14'09 N 15 29'29 E".replaceAll("[^A-Za-z0-9]", " ");
String[] array = input.split(" ");
int nDegree = Integer.parseInt(array[0]);
int nMinute = Integer.parseInt(array[1]);
int nSecond = Integer.parseInt(array[2]);
int eDegree = Integer.parseInt(array[4]);
int eMinute = Integer.parseInt(array[5]);
int eSecond = Integer.parseInt(array[6]);
double nDegrees = nDegree + (double) nMinute/60 + (double) nSecond/3600;
double eDegrees = eDegree + (double) eMinute/60 + (double) eSecond/3600;
String nResult = "Decimal = N " + Double.toString(nDegrees).substring(0,10);
String eResult = "Decimal = E " + Double.toString(eDegrees).substring(0,10);
System.out.println(nResult);
System.out.println(eResult);
}
}
Output:
Decimal = N 78.2358333
Decimal = E 15.4913888
The problem is that Java can't store the degrees ° character as part of a String, or internal quotes (the minute character). If you can find a way to remove them from the string before inputting the data, then this will work.
I don't have a solution for handling the degrees symbol, but you could use an escape symbol \" to allow the use of a quotation mark within a string.
So I've used a regex with capturing groups to grab each of the numbers and the N/S/E/W. After capturing each individually it's just a matter of doing a bit of dividing to get the numbers and then formatting them however you'd like. For example I went with 5 digits of precision here.
public static void main(String[] args) {
String coords = "78°14'09N 15°29'29E";
String[] decimalCoords = degreesToDecimal(coords);
System.out.println(decimalCoords[0]);
System.out.println(decimalCoords[1]);
}
public static String[] degreesToDecimal(String degMinSec) {
String[] result = new String[2];
Pattern p = Pattern.compile("(\\d+).*?(\\d+).*?(\\d+).*?([N|S|E|W]).*?(\\d+).*?(\\d+).*?(\\d+).*?([N|S|E|W]).*?");
Matcher m = p.matcher(degMinSec);
if (m.find()) {
int degLat = Integer.parseInt(m.group(1));
int minLat = Integer.parseInt(m.group(2));
int secLat = Integer.parseInt(m.group(3));
String dirLat = m.group(4);
int degLon = Integer.parseInt(m.group(5));
int minLon = Integer.parseInt(m.group(6));
int secLon = Integer.parseInt(m.group(7));
String dirLon = m.group(8);
DecimalFormat formatter = new DecimalFormat("#.#####", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
formatter.setRoundingMode(RoundingMode.DOWN);
result[0] = formatter.format(degLat + minLat / 60.0 + secLat / 3600.0) + " " + dirLat;
result[1] = formatter.format(degLon + minLon / 60.0 + secLon / 3600.0) + " " + dirLon;
}
return result;
}
There is no error handling here, it's just a basic example of how you could make this work with your input.
cardNumber should be an 16 character string e.g. 1111222233334444.
getCardNumber() should return this as the formatted version
e.g. 1111 2222 3333 4444.
How would I go about making sure that the String is 16 characters long, and then how would I space it out like in the example? Is there a way to use Substring() methods to do this?
Try regex like this :
public static void main(String... strings) {
String s = "1111222233334444";
System.out.println(s.matches("\\d{16}")); // check whether string contains exactly 16 digits
String newString = s.replaceAll("(\\d{4})", "$1"+" ").trim(); // replace every %4th digit by digit+" "
System.out.println(newString);
}
O/P :
true
1111 2222 3333 4444
You can use regex as shown by TheLostMind
Or, more simply:
String cardNumber = ...; // input
if (cardNumber.length() == 16) { // input is correct
return cardNumber.substring(0, 4) + " "
+ cardNumber.substring(4, 8) + " "
+ cardNumber.substring(8, 12) + " "
+ cardNumber.substring(12, 16);
} else {
// length is incorrect
}
Use String.length() to check the length of the number.
Then, let DecimalFormat do the formatting job for you:
DecimalFormat df = new DecimalFormat();
df.setGroupingSize(4);
df.setGroupingUsed(true);
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setGroupingSeparator(' ');
df.setDecimalFormatSymbols(dfs);
System.out.println(df.format(new BigDecimal("1234567890123456")));
If the input contains any non-numeric characters, BigDecimal will throw an exception.
You could try the below look-around based regex.
String s = "1111222233334444";
if(s.matches("\\d{16}"))
{
System.out.println(s.replaceAll("(?!^)(\\d{4})(?=(?:\\d{4})*$)", " $1"));
}
Output:
1111 2222 3333 4444
Regex Demo
Needlessly complicated version:
public static void main(String[] args) {
String cardNumber = "1111222233334444";
StringBuilder result = new StringBuilder();
if (cardNumber.length() != 16) {
System.out.println("Wrong length");
} else {
for (int i = 0; i < cardNumber.length(); i++) {
result.append(cardNumber.charAt(i));
if (i != 0 && (i + 1) % 4 == 0 && i != cardNumber.length() - 1)
result.append(" ");
}
System.out.println(result.toString()); //print result as string or use in method
}
}
}
For real, use regex or substring.
I got this code that fetches floats from a database.
for (int i = 0; i < ingredient.size() ; i++) {
Ingredient ing = (Ingredient) ingredient.get(i);
ingredients += String.valueOf(ing.getAmount()) + " " +
ing.getUnit() + " " + ing.getIngredient() + "\n";
}
The database is written in REAL values as some of them is 1.5, 2.5, 1.4 etc. But we also have these whole numbers without the need of a decimal, such as 1, 4, 10 etc.
The problem is that the database table needs to be in REAL value, which gives us no choice but to give all the values one decimal, no matter if it's needed or not.
So we'll end up with values like:
1.0
1.5
2.3
20.0
5.0
My question is: How do we remove the unnecessary decimals, but keep the ones that need it?
One very simple way to remove these would be to strip the characters using StringUtils.
String displayValue = String.valueOf(ing.getAmount());
displayValue = StringUtils.stripEnd(displayValue, ".0");
For an input of "1.0", "1" will be returned.
A more technical approach would be to use the modulus operator %
For example:
if(value%1 == 0){ //1 divides into number perfectly, there is no decimal
//cast value to integer or another non decimal variable
} else {
//use existing value as it contains a decimal
}
How about this (does't require any fancy things like StringUtils)?
String s = String.valueOf(1.0);
System.out.println(s);
/* Make this block as a function and return an int */
String ss = " ";
if (s.charAt(s.length()-2) == '.' && s.charAt(s.length()-1) == '0'){
ss = s.substring(0,s.length()-2);
System.out.println(ss);
}
/**************************************************/
int converted = Integer.parseInt(ss);
System.out.println(converted);
}
If you want to make it a function block, you can.
You can check it working on IDEONE - http://ideone.com/udJv8M
Check the float values with modulo. If 0 is returned it is an Integer. Here is an example with the numbers you have mentioned:
List<Float> values = new ArrayList<Float>();
values.add(new Float(1.0f));
values.add(new Float(1.5f));
values.add(new Float(2.3f));
values.add(new Float(20.0f));
values.add(new Float(5.0f));
List<String> strValues = new ArrayList<String>();
for(Float value : values)
{
String strValue = "";
if(value % 1 == 0)
{
Integer intValue = value.intValue();
strValue = intValue.toString();
strValues.add(strValue);
}
else
{
strValue = value.toString();
strValues.add(strValue);
}
System.out.println(strValue);
}
You can use a custom DecimalFormat pattern:
public static String customFormat(String pattern, double value) {
DecimalFormat myFormatter = new DecimalFormat(pattern);
return myFormatter.format(value);
}
Then a pattern of # defines places holders for optional digits, so #.### will give up to 3 digits where necessary only.
for (int i = 0; i < ingredient.size() ; i++) {
Ingredient ing = (Ingredient) ingredient.get(i);
ingredients += customFormat("#.###", ing.getAmount()) +
" " + ing.getUnit() + " " + ing.getIngredient() + "\n";
}
So don't convert your data to a String except for display only. Real numbers can represent both integers and floating point numbers using the same data type. Plus if you ever needed to do any math on your numbers you can't use Strings to do that. If you convert your numbers from the database directly to String before storing them into Ingredient then you've screwed yourself later on if you want to do calculations on those numbers. (Say you wanted to add a feature to double a recipe and have all of the quantities change for the user). Under your current plan you're preventing yourself from doing something like that because you're overly focused on the display of that number.
Instead just create a method on Ingredient to convert your numbers using String.format(). Like so:
public class Ingredient {
private double amount;
private String name;
public String asDecimal() {
return String.format("%.1f", amount);
}
public String asInteger() {
return String.format("%.0f", amount);
}
public String asFraction() {
// exercise left to the reader
}
}
You could even add a function that converts decimals to fractional amounts to make it easier to display things chiefs might understand vs decimals which are harder. Bear in mind String.format() will round floating point amounts (0.5 -> 1 using as Integer).
Convert your String returned from ing.getAmount() to a Float object, then use the modulo function to determine whether your value is an exact multiple of 1 (ie no decimal places). If so, convert your Float object to an int, which will concatenate the decimals.
Float f = Float.valueOf(ing.getAmount());
if(f%1 == 0) {
// Use f.intValue() to concatenate your decimals.
ingredients +=String.valueOf(f.intValue() + " " + ing.getUnit() + " " + ing.getIngredient() + "\n";
}
else {
ingredients +=String.valueOf(ing.getAmount()) + " " + ing.getUnit() + " " + ing.getIngredient() + "\n";
}
I hope this helps.