Restricting decimal places in an array - java

Currently, the output of this array has a too large decimal place trail. How can I restrict this to say 2 decimal places? By this I mean the array 'percentage1'. I've seen methods to do it online, but I don't understand how I would implement those methods into the code as shown below.
int[] correct1 = {20, 20, 13, 15, 22, 18, 19, 21, 23, 25};
int[] incorrect1 = {2, 1, 5, 2, 2, 5, 8, 1, 0, 0};
double[] percentage1 = new double[correct1.length];
for(int a = 0; a < correct1.length; a++ ){
percentage1[a] = (((double)correct1[a] / (correct1[a] + incorrect1[a]))*100);
}
Any help would be very much appreciated. Thanks

Please try adding a DecimalFormat object.
Add this to the beginning of the loop, it declares the format you're looking for - 2 decimal places: DecimalFormat df = new DecimalFormat("#.##");
Format it using format, then convert it back into a double. The reason why you need to restore it back is that format returns a String.
percentage1[a] = Double.valueOf(df.format((((double)correct1[a] / (correct1[a] + incorrect1[a]))*100)));
See revised code below:
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] correct1 = {20, 20, 13, 15, 22, 18, 19, 21, 23, 25};
int[] incorrect1 = {2, 1, 5, 2, 2, 5, 8, 1, 0, 0};
double[] percentage1 = new double[correct1.length];
DecimalFormat df = new DecimalFormat("#.##");
for(int a = 0; a < correct1.length; a++ ){
percentage1[a] = Double.valueOf(df.format((((double)correct1[a] / (correct1[a] + incorrect1[a]))*100)));
System.out.println(percentage1[a]);
}
}
Sample result:
90.91
95.24
72.22
88.24
91.67
78.26
70.37
95.45
100.0
100.0

You can't. Doubles don't have decimal places. They have binary places. If you want decimal places you have to use a decimal radix, i.e. a String created by DecimalFormat, or a BigDecimal.
Proof here.

Related

Getting frequencies and magnitudes from audio sample using FFT

I am currently working on a music visualization LED strip display (similar to this) but got stuck at the process of extracting audio frequency / magnitude from the sample data.
The entire process takes up quite a few steps, and I am not sure at which point I am failing exactly, so please bear with me. I start with reading audio from a TargetDataLine configured with AudioFormat.
AudioSystem.getTargetDataLine(new AudioFormat(8000.0f, 16, 1, true, true));
Next, I read the data as described in this tutorial. Only difference is that I am using a smaller buffer, effectively reading 64 bytes at a time.
Here is an example data read in this way from a 500Hz test tone.
final byte[] buffer = { // 32 16-bit samples
30, 111, 43, -19, 50, -74, 49, -54,
41, 70, 26, 121, 7, -90, -13, -89,
-31, -118, -44, 17, -51, 75, -50, 60,
-42, -62, -27, -115, -8, 91, 12, 85,
30, 106, 43, -34, 50, -93, 49, -76,
41, 52, 26, 108, 7, -93, -13, -84,
-31, -104, -44, 38, -51, 98, -50, 84,
-42, -42, -27, -98, -8, 99, 12, 83
};
Next, I transform the sampled bytes into an array of double values in range [-1, 1]. I took inspiration from the aforementioned tutorial and this code.
final double[] samples = new double[buffer.length];
int sampleIndex = 0;
int byteIndex = 0;
while (byteIndex < buffer.length) {
int low = buffer[byteIndex];
++byteIndex;
int high = buffer[byteIndex];
++byteIndex;
int sampleIntValue = ((high << 8) + (low & 0x00ff));
double maxSampleValue = 32768;
samples[2 * sampleIndex] = ((double) sampleIntValue) / maxSampleValue; // Transforming to [-1, 1]
samples[(2 * sampleIndex) + 1] = 0; // Imaginary part
++sampleIndex;
}
Next, I perform a Fast Fourier Transform on the data using the edu.emory.mathcs:JTransforms:2.4 library.
final DoubleFFT_1D fft = new DoubleFFT_1D(samples.length / 2);
fft.complexForward(samples);
Then, I calculate frequencies and magnitudes as described in this code.
final float sampleRate = 8000f;
final Map<Double, Double> frequenciesToMagnitudes = IntStream.range(0, samples.length / 2)
.boxed()
.collect(Collectors.toMap(
index -> 2 * ((double) index / (double) samples.length) * sampleRate,
index -> Math.log10(
Math.sqrt(
Math.pow(samples[2 * index], 2) // real part
+ Math.pow(samples[(2 * index) + 1], 2) // imaginary part
)
)
));
I find the maximum magnitude so I can scale the displayed values accordingly.
final double maximumMagnitude = frequenciesToMagnitudes.values().stream().max(Double::compare).orElse(0d);
And finally, I display the results (taller block represents a brighter LED).
final char[] magnitudeDisplay = {'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'};
frequenciesToMagnitudes.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(frequencyToMagnitude -> {
final double magnitudePercentage = frequencyToMagnitude.getValue() / maximumMagnitude;
final int characterToDisplayIndex = Math.min(magnitudeDisplay.length - 1, Math.max(0, (int) (magnitudePercentage * magnitudeDisplay.length)));
final char characterToDisplay = magnitudeDisplay[characterToDisplayIndex];
System.out.print(characterToDisplay);
});
System.out.println();
From all this, I would expect to see two distinct spikes (my frequency + alias region), but instead there is 8.
▁▁▅▁▁▁█▁▁▁▄▁▁▁▅▁▁▁▅▁▁▁▄▁▁▁█▁▁▁▅▁
The number of spikes changes depending on frequency of audio playback (sometimes it's two, sometimes it's four), and the lower the frequency the more spikes I see.
My question is: how do I extract frequency / magnitude pairs from audio data?
After a bit of digging I have found the OpenIMAJ library which does exactly what I need. Here are the differences in my code and their code.
Transforming from bytes to floating point is incorrect. All examples use big endian audio format, but take the bytes in the wrong order. It should be:
int high = buffer[byteIndex];
++byteIndex;
int low = buffer[byteIndex];
++byteIndex;
Size of FFT output is padded to next power of 2. This was not a problem since I always chose a buffer of size that was a power of 2, but in case if your buffer is not, this will be necessary.
final int fftSize = (int) Math.pow(2, 32 - Integer.numberOfLeadingZeros(numberOfSamples - 1));
The values are transformed to floating point a bit differently.
samples[2 * sampleIndex] = (float) sampleIntValue * Integer.MAX_VALUE / Short.MAX_VALUE;
Real parts are normalized.
for (int i = 0; i < samples.length; i += 2) samples[i] /= fftSize;
No logarithms are used for fetching magnitudes.
final Map<Float, Float> frequenciesToMagnitudes = IntStream.range(0, samples.length / 4)
.boxed()
.collect(Collectors.toMap(
index -> (((float) (index * 2)) * (sampleRate / (float) numberOfSamples)) / 2,
index -> {
final float realPart = samples[index * 2];
final float imaginaryPart = samples[index * 2 + 1];
return (float) Math.sqrt(realPart * realPart + imaginaryPart * imaginaryPart);
}
));
After all the changes, the output appears to be much more reasonable:
▁▁▂▁▁▁█▁▁▁▂▁▁▁▂▁
I am still not entirely sure if the frequency calculation is correct, though.

Java Iterating into array for different orientations

I'm stitching a bitmap from a layout array, that puts the a larger bitmap together as a guide. Using multiple bitmaps into one bitmap. What I rotate the whole bitmap, my solution is to restitch it but have the array account for this rotation. The making of the bitmap is determined by the order of the array. The stitching assumes that the array starts from zero and that is the first index or the left top most corner and then goes to the right to the end and goes to beginning of the next row. I would like to have a 90, 180, 270, and 360 functions to call on. I'm thinking 360 is easy I iterate backwards. I'm using 11 by 11 which is constant.
For example
0, 1, 3, 4
5, 6, 7, 8,
9, 10, 11, 12
13, 14, 15, 16
when I rotate 90
4, 8, 12, 16
3, 7, 11, 15
1, 6, 10, 14
0, 5, 9, 13
Try this. This may have performance impact but its a simple solution.
import java.util.Arrays;
public class RotateMatrix {
public static void main(String[] args) {
int original[][] = { { 0, 1, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 },
{ 13, 14, 15, 16 } };
printArray(original);
System.out.println("After 90 Degree");
printArray(rotate(original, 90));
System.out.println("After 180 Degree");
printArray(rotate(original, 180));
System.out.println("After 270 Degree");
printArray(rotate(original, 270));
System.out.println("After 360 Degree");
printArray(rotate(original, 360));
}
private static int[][] rotate(int[][] original, int angle) {
int[][] output = original;
int times = 4 - angle/90;
for(int i=0; i<times; i++){
output = rotate(output);
}
return output;
}
private static int[][] rotate(int[][] original) {
int n = original.length;
int m = original[0].length;
int[][] output = new int[m][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
output[j][n - 1 - i] = original[i][j];
return output;
}
private static void printArray(int[][] array) {
for (int i = 0; i < array.length; i++) {
System.out.println(Arrays.toString(array[i]));
}
}
}
This rotates the array anti-clockwise direction just as in your example. However if you want to change this to clockwise just change int times = 4 - angle/90; to int times = angle/90; in rotate(int[][] original, int angle) method.

How to use SharedPreferences in Achartengine line graph?

I want to use SharedPreferences in the code for LineGraph:
public class LineGraph{
Context applicationContext;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
public Intent getIntent(Context context) {
// Our first data
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // x values!
int[] y = { 30, 34, 45, 57, 77, 89, 100, 111 ,123 ,145 }; // y values!
TimeSeries series = new TimeSeries("Line1");
for( int i = 0; i < x.length; i++)
{
series.add(x[i], y[i]);
}
// Our second data
int[] x2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // x values!
int[] y2 = { 145, 123, 111, 100, 89, 77, 57, 45, 34, 30}; // y values!
TimeSeries series2 = new TimeSeries("Line2");
for( int i = 0; i < x2.length; i++)
{
series2.add(x2[i], y2[i]);
}
XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
dataset.addSeries(series);
dataset.addSeries(series2);
XYMultipleSeriesRenderer mRenderer = new XYMultipleSeriesRenderer(); // Holds a collection of XYSeriesRenderer and customizes the graph
mRenderer.setZoomButtonsVisible(true);
XYSeriesRenderer renderer = new XYSeriesRenderer(); // This will be used to customize line 1
XYSeriesRenderer renderer2 = new XYSeriesRenderer(); // This will be used to customize line 2
mRenderer.addSeriesRenderer(renderer);
mRenderer.addSeriesRenderer(renderer2);
// Customization time for line 1!
renderer.setColor(Color.WHITE);
renderer.setPointStyle(PointStyle.SQUARE);
renderer.setFillPoints(true);
// Customization time for line 2!
renderer2.setColor(Color.YELLOW);
renderer2.setPointStyle(PointStyle.DIAMOND);
renderer2.setFillPoints(true);
Intent intent = ChartFactory.getLineChartIntent(context, dataset, mRenderer, "Line Graph Title");
return intent;
}
}
But the SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
does not work, it stopped the application. Why?
Call this within your method, not at the class level.
Something like this:
public class LineGraph {
public Intent getIntent(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// Our first data
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // x values!
...
The way you had it, applicationContext was always going to be null.
Please provide your logcat-output.
Apart from this I assume that you receive a nullpointer exception when itializing the shared preferences, because the applicationContext-variable is null.
Place the initialization of your shared preferences into the getIntent()-method and pass the context-argument.

My table is outputting incorrect values for the percentage

public class apples {
private static String[] level1 = new String[] { "A", "B", "I", "K", "N", "O", "P", "S", "T", "W" };
public static void main(String[] args) {
int[] scores1 = { 99, 80, 56, 88, 70, 35, 67, 60, 78, 56 };
int[] correct1 = {20, 20, 13, 15, 22, 18, 19, 21, 23, 25};
int[] incorrect1 = {2, 1, 5, 2, 2, 5, 8, 1, 0, 0};
double[] percentage1 = new double[correct1.length];
for(int a = 0; a < correct1.length; a++ ){
percentage1[a] = (double)((correct1[a] / (correct1[a] + incorrect1[a]))*100);
}
System.out.println("Character \t Correct \t Incorrect \t Percentage");
for(int counter = 0; counter<scores1.length;counter++){
System.out.println(level1[counter] + "\t\t " + correct1[counter] + "\t\t " + incorrect1[counter] + "\t\t " + percentage1[counter]);
}
}
}
This outputs a table with 4 headings. The character, correct and incorrect columns show as expected. However the percentage row is not working properly. For example, character 'A', correct 20 and incorrect 2 gives a percentage of 0.0. Any 'incorrect' value > 0 outputs a percentage value of 0, and any 'incorrect' value which = 0 gives a percentage value of 100 (which is correct)... Can someone please explain where I have gone wrong?
You are dealing with integers here, and for integer division, the result is truncated. You'll need to cast the original values to double instead, or multiply one part by 1.0 to get it as a double:
percentage1[a] = ((correct1[a]*1.0 / (correct1[a] + incorrect1[a]))*100);
percentage1[a] = (double)((correct1[a] / (correct1[a] + incorrect1[a]))*100);
The above code casts to a double after the calculation is competed.
To cast as part of the calculation, use:
percentage1[a] = (( ((double)correct1[a]) / (correct1[a] + incorrect1[a]))*100);
You calculations here
percentage1[a] = (double)((correct1[a] / (correct1[a] + incorrect1[a]))*100);
perform integer division (you just cast them afterwards to double). If you want them to return the actual floating point division result, you have to cast all operands to double before the calculation.
So the fastest option would be to change this:
double[] correct1 = {20, 20, 13, 15, 22, 18, 19, 21, 23, 25};
double[] incorrect1 = {2, 1, 5, 2, 2, 5, 8, 1, 0, 0};
Another would be to change the computation to something like this
percentage1[a] = (1.0 * correct1[a] / (correct1[a] + incorrect1[a]))*100;
or to simplify a little:
percentage1[a] = 100.0 * correct1[a] / (correct1[a] + incorrect1[a]);

Using Number Randoms in Arrays that randomize

I am making a small class for a little project. It's a text based RPG game and I am trying to create a drop class for when NPC's die. I have create a couple Math.random methods (which are exactly the same, just differently named for my convenience.) to drop random amounts for an item id (name) and to get the rarity of the item dropped. It all works fine, but it only randomizes one time (on startup or run) and it won't randomize the amounts after that. I am also randomizing it between 2 numbers, for example, 25 and 50, the random not going lower then 25 or higher then 50.
My question is: How can I randomize a integer in a 2D Array or a general array after each time a NPC dies, so the random number that is first obtained changes and doesn't stay the same. Because right now, it stays at the number choose. if the number is 25, then the next npc I kill, the amount would still be 25.. and 25.. and 25.. and so on. I need it to randomize or change.
Please help, thank you.
public class DropConfig {
private static final int
ALWAYS = 0,
VERY_COMMON = rate(1, 9),
COMMON = rate(10, 20),
UNCOMMON = rate(30, 40),
RARE = rate(50, 60),
VERY_RARE = rate(70, 80),
SUPER_RARE = rate(90, 100);
public static final int[][] NPC_DROPS = {
// Normal NPC's
{1, 526, 1, ALWAYS},
{2, 526, 1, ALWAYS},
{3, 526, 1, ALWAYS},
{1, 995, drop(1, 50), ALWAYS},
{2, 995, drop(1, 50), ALWAYS},
{3, 995, drop(1, 50), ALWAYS},
// Moderate NPC's
{9, 526, 1, ALWAYS},
{9, 995, drop(250, 500), UNCOMMON},
{9, 555, drop(2, 7), VERY_COMMON},
{9, 995, drop(5, 50), VERY_COMMON},
{9, 1050, 1, RARE},
};
public static int rate(int min, int max) {
return 1 + (int)(Math.random() * ((max - min) + 1));
}
//Same as rate, different name for looks.
public static int drop(int min, int max) {
return 1 + (int)(Math.random() * ((max - min) + 1));
}
Heres where I call the drops method
public void npcDeath() {
int npc = 0;
if (npc == null)
return;
for(npc = 0; npc < DropConfig.NPC_DROPS.length; npc++) {
if(npc == DropConfig.NPC_DROPS[npc][0]) {
if(Misc.random(DropConfig.NPC_DROPS[npc][3]) == 0) { //Drops ALWAYS item
Item(DropConfig.NPC_DROPS[npc][1], DropConfig.NPC_DROPS[npc][2]);
}
}
}
}
If I understand correctly, you would like the elements of the NPC_DROPS array initialized with a call to drop() to be reinitialized each time this NPC_DROPS array is used.
Well, NPC_DROPS is a constant, so it can't change. Generate it each time it's accessed, using a method:
public static int[][] generateNpcDrops(){
return new int[][] {
// Normal NPC's
{1, 526, 1, ALWAYS},
{2, 526, 1, ALWAYS},
{3, 526, 1, ALWAYS},
{1, 995, drop(1, 50), ALWAYS},
{2, 995, drop(1, 50), ALWAYS},
{3, 995, drop(1, 50), ALWAYS},
// Moderate NPC's
{9, 526, 1, ALWAYS},
{9, 995, drop(250, 500), UNCOMMON},
{9, 555, drop(2, 7), VERY_COMMON},
{9, 995, drop(5, 50), VERY_COMMON},
{9, 1050, 1, RARE},
}
}
...
public void npcDeath() {
int npc = 0;
if (npc == null)
return;
int[][] npcDrops = DropConfig.generateNpcDrops();
for(npc = 0; npc < npcDrops.length; npc++) {
if(npc == npcDrops[npc][0]) {
if(Misc.random(npcDrops[npc][3]) == 0) { //Drops ALWAYS item
Item(c, npcDrops[npc][1], npcDrops[npc][2]);
}
}
}
}
You want to place functions as constants. You can do this in a language like Scala naturally, but in Java you have to work a little harder.
In every case, you need to call a function to get the actual value.
You can use enum and anonymous methods, but the simplest way/hack is to encode your ranges.
public static int rate(int min, int max) { // same for drop.
int range = max - min;
return (range << 16) | (min & 0xFFFF);
}
public static int eval(int minMax) {
int min = (short) minMax;
int range = (short) (minMax >> 16);
if (range == 0)
return min; // plain number.
else
return min + (int) (Math.random() * (range + 1));
}
You need to call eval() to turn your encoded range into a random number.
Creating an instance of Random with the same seed you will get the same 'random' number. Have you considered using SecureRandom? The difference between SecureRandom and Random is that SecureRandom produces non-deterministic output on each call.

Categories