How to best print 2 float numbers in scientific-notation but with same exponent?
eg: I'd like to print numbers like this:
1.234e-6
11.234e-6
And I would like some function to detect automatically best exponent - that is smaller number always start at first decimal digit and bigger number prints how it must with same exponent.
eg: 0.1 and 100 would print
1.000e-1
1000.000e-1
But even when I ask explicitly for 2 decimal places String.format("%2.3e",11.234e-6) I got 1.123e-5
So far I come up with code bellow. It works as I want. But as you can see it is not exactly short or swift... It would be great if someone would point to some Java native functions which helps me to do it more elegant...
public static String repeat(int count, String with) {
if(count<0){
return "";
}
if(count>1e5){
return "";
}
return new String(new char[count]).replace("\0", with);
}
public static String getFormattedDouble(double num,int posInt,int posFrac){
if(num == 0) return "0"; // correct 0 value to be print only with one 0
String sInt = repeat(posInt,"0");
String sFrac = repeat(posFrac,"0");
String sSing = num<0 ? "" : "+";
DecimalFormat form = new DecimalFormat(sSing+sInt+"."+sFrac+"E0");
String s = form.format(num);
s = s.replace("E","e"); // I really thing capital E looks ugly
return s;
}
public static String[] get2doublesSameExp(double a, double b){
String[] s = new String[2];
int expA;
if(a == 0) expA = 0;
else expA = (int)Math.floor(Math.log10(Math.abs(a)));
int expB;
if(b == 0) expB = 0;
else expB = (int)Math.floor(Math.log10(Math.abs(b)));
double expDif = Math.abs((double)expA-(double)expB);
int fractPos = 3;
if(expDif > 4) fractPos = 1; // too big exponent difference reduce fraction digits
if(expDif > 6){
// too big exponent difference print it normally it will be nicer
s[0] = String.format("%1.3e",a);
s[1] = String.format("%1.3e",b);
return s;
}
int minExp = Math.min(expA,expB) - 1;
s[0] = getFormattedDouble(a, expA - minExp, fractPos );
s[1] = getFormattedDouble(b, expB - minExp, fractPos );
// just text right justification
String rightJust = repeat((int)expDif," ");
int justIdx = expA < expB ? 0 : 1;
s[justIdx] = rightJust + s[justIdx];
return s;
}
String[] s = get2doublesSameExp(1.234e-6,11.234e-6);
Related
I tried coding a complete decimal to binary converter, which worked just fine and now I want to remove the unnecessary 0s from my output String, but it doesnt remove all of the 0s and I have no idea why.
Here is my code :
public class Converter {
private static final int[] ARRAY = {16348,8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1};
public static String toBinary(int number) {
String output = new String();
int number2 = number;
for (int i=0; i<ARRAY.length ; i++) {
if (number2 - ARRAY[i] >= 0) {
output += "1";
number2 -= ARRAY[i];
}
else {
output += "0";
}
}
boolean sorted = false;
int i = 0 ;
while (sorted == false) {
if (output.charAt(i) == '0') {
StringBuilder temp = new StringBuilder(output);
temp.deleteCharAt(i);
output = temp.toString();
i ++;
}
else{
sorted = true;
}
}
return output;
}
}
It's because the variable i is pointing to a wrong location. It's because after you remove the 0 at the ith you are incrementing i.
For example, take the string 00001 (a minified one in terms of number of bits)
When i = 0, after removing the 0 at index i we get 0001. On incrementing i becomes 1.
When i = 1, after removing the 0 at index i we get 001. On incrementing i becomes 2.
(We have already skipped past the first 0).
Removing i++ will do the trick. Still, your code does not work for input 0. In your while loop, you still need to validate the index i and break the while loop by checking if output string is empty.
A simple solution would be to use Integer.valueOf as suggested in this answer.
Or another one-liner would be:
String result = output.contains("1") ? output.substring(output.indexOf("1")) : "0";
I suggest to use simpler solution to remove trailing zero's by parsing the output to Integer
use this
int x = Integer.valueOf(output);
// if you wanted the output as string
String val = String.valueOf(x);
It is preferable that you don't add the leading zeroes in the first place. Why create, then remove? This method will not add leading zeroes until a one has been detected:
public static String toBinary(int number) {
String output = new String();
int number2 = number;
boolean foundOne = false;
for (int i=0; i<ARRAY.length ; i++) {
if (number2 - ARRAY[i] >= 0) {
output += "1";
number2 -= ARRAY[i];
foundOne = true;
}
else if (foundOne){
output += "0";
}
}
if (!foundOne)
output = "0";
return output;
}
String s = "00010101"
System.out.println(s.replaceAll("^0*", ""))
// => 10101
This is a example using regex.
^ means the expression should only match at the beginning of a string.
0*matches any string of characters that repeats 0 zero or more times
This is for a fraction class. When i test the fraction class with a string like "2/4" I get the following exception:
java.lang.NumberFormatException: For input string: "".
I think it has something to do with the sStringTo method when trying to replace spaces.
public class Util{
static int findGCF(int a, int b){
a = Math.abs(a);
b = Math.abs(b);
while(a != b){
if (a>b) a = a-b; else b = b-a;
}
return (a);
}
static Fraction sIntTo(String s){ //"2"
int n = Integer.parseInt(s);
return new Fraction(n);
}
static Fraction sFractionTo(String s){ //"2/3"
s = s.trim();
int posSlash = s.indexOf("/");
int n = Integer.parseInt(s.substring(0,posSlash));
int m = Integer.parseInt(s.substring(posSlash + 1));
return new Fraction(n,m);
}
static Fraction sMixTo(String s){
s =s.trim();
int posB = s.indexOf(" ");
int posSlash = s.indexOf("/");
int w = Integer.parseInt(s.substring(0,posB));
int t = Integer.parseInt(s.substring(posB+1, posSlash));
int b = Integer.parseInt(s.substring(posSlash+1));
return new Fraction(w*b+t,b);
}
static Fraction sDecTo(String s){
s = s.trim();
int i = s.indexOf(".");
String sub = s.substring(i+1);
String sNoPeriod = s.substring(0,i) + sub;
int top = Integer.parseInt(sNoPeriod);
int bot = 1;
for(int j = 0; j<sub.length(); j++) bot = bot*10;
return new Fraction(top,bot);
}
static Fraction divFraction(Fraction f, Fraction g){
return new Fraction (f.num * g.den, f.den * g.num);
}
static Fraction addFraction(Fraction f, Fraction g){
return new Fraction (f.num * g.den + f.den * g.num, f.den*g.den);
}
static Fraction sStringTo(String s){
s=s.trim();
s= s.replaceAll("\\s*/\\S*", "/");// remove 0 or more blanks before & after slash
s = s.replaceAll("\\s+", " "); // all blanks to be one blank: 2 1/2
int posB = s.indexOf(" ");
int posSlash = s.indexOf("/");
int posPed = s.indexOf(".");
Fraction ans = null;
if(posB>-1){
if (posSlash>posB) { ans = sMixTo(s);}
}else{
if (posPed == -1 && posSlash == -1) ans = sIntTo(s); //integer only
else{
if(posSlash == -1) ans = sDecTo(s);// decimal only
else{
//with slash "2.1/2 2.1/2.1
Fraction f = sStringTo(s.substring(0,posSlash));
Fraction g = sStringTo(s.substring(posSlash+1));
ans = divFraction(f,g);
}
}
}
return ans;
}//sStringTo()
The problematic stack trace is:
Exception in thread "main" java.lang.NumberFormatException: For input string: ""
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Integer.parseInt(Integer.java:662)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at Util.sIntTo(Util.java:17)
at Util.sStringTo(Util.java:75)
at Util.sStringTo(Util.java:81)
at Fraction.<init>(Fraction.java:31)
at Test.main(Test.java:6)
It looks like you are passing a string containing only whitespace into sIntTo(). You should consider just removing all whitespace, rather than just reducing it to a single space. Consider changing s = s.replaceAll("\\s+", " "); to s = s.replaceAll("\\s+", "");.
I figured out the problem. It was a syntax error on my part.
I am trying to convert a number from decimal value to its IEEE 752 form. For example:
+43.9542 -> 01000010001011111101000100011010
-43.9542 -> 11000010001011111101000100011010
And I have written this method:
public float GetFloat32(String numero) {
//Convert
int intBits = Integer.parseInt(numero, 2);
float myFloat = Float.intBitsToFloat(intBits);
//Exponent
getExponent = 127 + (String.valueOf(Integer.toBinaryString(Math.abs((int) myFloat))).length() - 1);
//Sign
getSign = (myFloat < 0) ? 1 : 0;
return myFloat;
}
There is a problem that I cannot solve. I will make another example to make it clear.
double a = k.GetFloat32("01000010001011111101000100011010")
a = 43.9542
But when the number is negative, such as
double a = k.GetFloat32("1000010001011111101000100011010")
I get this error:
It means that my code works perfectly with positive numbers (including the zero) but with negative numbers it crashes. Where is the problem?
Note
I thought that I could solve my problem in this way
Check if String numero has a charAt(0) equal at 1
If yes (numero.charAt(0) == 1) then remove the first char
Call GetFloat32() with the new number (without the 1)
Return the result adding the - in front of the num
This could work but I would like to know where is the problem in the method above. I'd prefer avoiding this solution if possible.
The problem is that int/Integer has an upper limit of 0x7FFF_FFFF and so the Integer.parseInt method won't go beyond this limit.
Use Long.parseLong, check the resulting value for exceeding 0x7FFF_FFFFL and handle according to the logic required for negative integer values.
Although, I don't see anything bad in your very own idea of stripping the sign bit and dealing with the rest to get the absolute value.
Edit There isn't a way to get the encoded float with a simple integer conversion from a bit string. Just consider that +1 and -1 in 32 binary digits according to two's complement representation differ in more than one bit, and 100....000 isn't -0. Signed magnitude isn't the same as two's complement. Moreover, Java's binary and hexadecimal (or any other base's) literals are meant to be positive quantities; if you need a negative value, use a sign.
Later Method Integer.parseUnsignedInt doesn't have an advantage over using Long, since you'll then have to know how use two's complement arithmetic to remove the leading (sign) bit to produce the absolute value which can then be split into exponent and mantissa. (Add or subtract Integer.MIN_VALUE.)
If there isn't IEEE 752 and IEEE 754 is the target format, the easiest form is
float f1 = Float.intBitsToFloat(Integer.parseUnsignedInt(numerio,2));
public class IEEE754ToFloatingValues {
public static double convertToInt(String mantissa_str) {
int power_count = -1;
double mantissa_int = 0.0;
for (int i = 0; i < mantissa_str.length(); i++) {
// System.out.print(Integer.parseInt(mantissa_str.charAt(i) + ""));
mantissa_int += (Integer.parseInt(mantissa_str.charAt(i) + "") * Math.pow(2, power_count));
power_count -= 1;
}
IEEE754ToFloatingValues.logger.info((Object) "IEEE754ToFloatingValues : convertToInt :: end");
return mantissa_int + 1.0;
}
public static String convertToBinary(int i) {
return Integer.toBinaryString(i + 0b10000).substring(1);
}
public static String decimalToHex(String decimal) {
int i = Integer.parseInt(decimal);
System.out.println("<<>>" + i);
String my_hexdata = Integer.toHexString(i);
System.out.println(my_hexdata);
return String.valueOf(ReturnFloatingValue(my_hexdata));
}
public static double ReturnFloatingValue(String my_hexdata) {
String myString = "";
if (my_hexdata == null) {
return -2.0;
}
if (my_hexdata.length() != 8) {
myString = String.format("%1$-" + 8 + "s", my_hexdata).replace(' ', '0');
System.out.println("My hex data after appending 0's is : " + myString);
}
String binary = "";
for (int i = 0; i < myString.length(); i++) {
int num = Integer.parseInt(myString.charAt(i) + "", 16);
binary += convertToBinary(num);
}
System.out.println("Binary length is : " + binary.length());
System.out.println("Binary number is : " + binary);
if (binary == null || binary.isEmpty()) {
return -3.0;
}
String ieee_32 = binary.substring(2);
ieee_32 = String.format("%1$-32s", binary).replace(' ', '0');
long sign_bit = Long.parseLong(new StringBuilder().append(ieee_32.charAt(0)).toString());
long exponent_bias = Long.parseLong(ieee_32.substring(1, 9), long exponent_unbias = exponent_bias - 127L;
System.out.println("Exponent unbias is : " + exponent_unbias);
String mantissa_str = ieee_32.substring(9);
double mantissa_int = convertToInt(mantissa_str);
double real_no = Math.pow(-1.0, (double) sign_bit) * mantissa_int * Math.pow(2.0, (double) exponent_unbias);
System.out.println("Real no is : " + real_no);
return real_no;
}
public static void main(String[] args) {
//decimalToHex("0");
}
}
import java.util.*;
public class IEEE754 {
public static void main(String[] args) {
float floa;
Scanner sc = new Scanner(System.in);
System.out.println("ENTER A FLOATING POINT NUMBER");
floa=sc.nextFloat();
int no,sign;
no=(int) floa;
String a= Integer.toBinaryString(no);
String sub=a.substring(1, a.length());
if(floa<0)
{
sign=1;
}
else
{
sign=0;
}
int exp=a.length()-1;
System.out.println(exp);
int be=127+exp;
String b= Integer.toBinaryString(be);
System.out.print("biased exponent->");
System.out.println(b);
int loop=23-exp;
float floatpart =floa-no;
int q[]=new int[25];
for(int i=1,j=0;i<=loop;i++,j++)
{
q[j]=(int) (floatpart*2);
floatpart=floatpart*2;
if(q[j]==1){
floatpart=floatpart-1;
}
}
System.out.print("mantissa->");
System.out.print(sub);
for(int k=0;k<q.length;k++)
System.out.print(q[k]);
System.out.println();
System.out.println("IEEE754 FORMAT IS");
System.out.print(sign+" "+b+" ");
System.out.print(sub);
for(int k=0;k<q.length;k++)
System.out.print(q[k]);
}
}
Is it possible in Java to efficiently read an integer from random position of the string? For instance, I have a
String s = "(34";
if (s.charAt(0) == '(')
{
// How to read a number from position = 1 to the end of the string?
// Of course, I can do something like
String s1 = s.substring(1);
int val = Integer.parseInt(s1);
}
but it dynamically creates a new instance of string and seems to be too slow and performance hitting.
UPDATE
Well, to be precise: I have an array of strings in form "(ddd" where d is a digit. So I do know that a number starts always from pos = 1. How do I efficently read these numbers?
Integer.parseInt(s1.replaceAll("[\\D]", ""))
Answered before the update:
I'm not an expert in regex, but hope this "\\d+" is useful to you. Invoke the below method with pattern: "\\d+".
public static int returnInt(String pattern,String inputString){
Pattern intPattern = Pattern.compile(pattern);
Matcher matcher = intPattern.matcher(inputString);
matcher.find();
String input = matcher.group();
return Integer.parseInt(input);
}
Answered after the update:
String is a final object, you cannot edit it, so if you want to get some digit value from it, you have the 2 ways:
1. Use your code, that will work fine, but if you care about performance, try 2nd way.
2. Divide your string on digits and add them to get the result:
public static void main(String[] args) {
String input = "(123456";
if(input.charAt(0) == '(') {
System.out.println(getDigit(input));
}
}
private static int getDigit(String s) {
int result = 0;
int increase = 10;
for(int i = 1; i < s.length(); i++) {
int digit = Character.getNumericValue(s.charAt(i));
result*=increase;
result += digit;
}
return result;
}
Output:
123456
If you don't want to allocate a new String then you can use the code in this other SO answer:
int charArrayToInt(char[] data, int start, int end) throws NumberFormatException {
int result = 0;
for (int i = start; i < end; i++) {
int digit = ((int)data[i] & 0xF);
if ((digit < 0) || (digit > 9)) throw new NumberFormatException();
result *= 10;
result += digit;
}
return result;
}
You can call it with charArrayToInt(s.toCharArray(), 1, s.length())
I have a number as a string like this: "9.756088256835938E-4" but I only can use a specified number of characters (in this special case 9 char). So I want to have something like this: "9.7561E-4". I already tried to convert the String to a Double and then used the format method to get a less characters but I don't got a correct solution.
The problem is that I need ten exponential output since some numbers are longer than the number of characters I have. If it is possible, the number should be displayed with no ten exponent, if not just use the ten exponent.
Also correct rounding would be good.
And it should work for negative numbers. (minus needs one character!!!)
Is there a format function where I can define the maximum length of the output string? Any ideas?
I'm having trouble findind a single format pattern that will cover all of the cases that you described. But here's a combination of logic that I think works:
public static void main(String[] args) throws Exception {
// 97.560883
System.out.println(formatNum(Double.parseDouble("9.756088256835938E+1")));
// 9.756E+11
System.out.println(formatNum(Double.parseDouble("9.756088256835938E+11")));
// 0.0009756
System.out.println(formatNum(Double.parseDouble("9.756088256835938E-4")));
// -9.8E+111
System.out.println(formatNum(Double.parseDouble("-9.756088256835938E+111")));
}
private static final int MAX_LENGTH = 9;
private static String formatNum(double number) {
String out = null;
for ( int i = 0; i < MAX_LENGTH; i++ ) {
String format = "%." + i + "G";
out = String.format(format, number);
if ( out.length() == MAX_LENGTH ) {
return out;
}
}
return out; //the best we can do
}
The "G" in the pattern instructs the formatter to forego the use of the exponent when it will allow for the same or better precision. We grow up to the maximum length and stop when our output string is 10 characters. I think you could take the same approach with a DecimalFormat, but I'm more familiar with Formatter.
Seeing the Mark's example meet your requirements, I updated my answer to show the DecimalFormat implementation. I used Mark's test cases. It is definitely an uglier option because there is no easy way to turn on/off exponents. The only advantage over the String.format option is that it handles very small numbers well.
public static void main(String[] args) throws Exception {
// 97.560883
System.out.println(formatNum(Double.parseDouble("9.756088256835938E+1")));
// 9.756E+11
System.out.println(formatNum(Double.parseDouble("9.756088256835938E+11")));
// 0.0009756
System.out.println(formatNum(Double.parseDouble("9.756088256835938E-4")));
// -9.8E+111
System.out.println(formatNum(Double.parseDouble("-9.756088256835938E+111")));
}
private static final int MAX_LENGTH = 9;
private static String formatNum(double number) {
int digitsAvailable = MAX_LENGTH - 2;
if (Math.abs(number) < Math.pow(10, digitsAvailable)
&& Math.abs(number) > Math.pow(10, -digitsAvailable)) {
String format = "0.";
double temp = number;
for (int i = 0; i < digitsAvailable; i++) {
if ((temp /= 10) < 1) {
format += "#";
}
}
return new DecimalFormat(format).format(number);
}
String format = "0.";
for (int i = 0; i < digitsAvailable; i++) {
format += "#";
}
String r = new DecimalFormat(format + "E0").format(number);
int lastLength = r.length() + 1;
while (r.length() > MAX_LENGTH && lastLength > r.length()) {
lastLength = r.length();
r = r.replaceAll("\\.?[0-9]E", "E");
}
return r;
}
This reminded me of a similar question where the OP only had 5 or so spaces for a number and wanted to show a decimal only when there was enough space. But instead of exponents, wanted to use a suffix of (k,m, etc)