I've been making an image editor in my free time and tried to use the HSL to RGB conversion formula here: HSL to RGB color conversion however I have come across an error.
Whenever I got to run my program the conversion does not work as it should, leaving the rgb with an unexpected grey colour. Here's some sample output data:
HSL: 0.0, 0.0, 1.0
RGB: 255.0, 255.0, 255.0
HSL: 214.0, 0.73, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.74, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.75, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.76, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.76, 0.5
RGB: 128.0, 128.0, 128.0
And below is my code. It's all written in Java.
public double[] hslToRgb(double h, double s, double l){
System.out.println("HSL: " + h + ", " + s + ", " + l);
double r = -1;
double b = -1;
double g = -1;
if(s == 0){
r = l;
b = l;
g = l;
}else{
double q = 1 < 0.5 ? l * (1 + s) : l + s - 1 * s;
double p = 2 * l - q;
r = hueToRgb(p, q, h + (1 / 3));
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - (1/ 3));
}
r = Math.round(r * 255);
b = Math.round(b * 255);
g = Math.round(g * 255);
System.out.println("RGB: " + r + ", " + g + ", " + b);
double[] rgb = {r, g, b};
return rgb;
}
private double hueToRgb(double p, double q, double t){
if(t < 0){
t++;
}
if(t > 1){
t--;
}
if(t < 1 / 6){
return p + (q - p) * 6 * t;
}
if(t < 1 / 2){
return q;
}
if(t < 2 / 3){
return p + (q - p) * ((2 / 3) - t) * 6;
}
return p;
}
Can anybody give me some insight as to what might be causing this? I feel like it's a minor error in my code but I can't find it anywhere.
Firstly, in the line double q = 1 < 0.5 ..., it's supposed to be the letter EL l, not the number 1 in two places.
double q = 1 < 0.5 ? l * (1 + s) : l + s - 1 * s;
^ ----- change from 1 to l ---- ^
Secondly, you have to be very careful about arithmetic which works differently in Java than JavaScript. When you have code like 1 / 3, since both are integers, the result will be an integer which means you get 0 rather than 0.3333.... In order to prevent using integer division, all of your divisions need to use at least one floating-point type:
if(t < 1.0 / 6){
^^^ change from 1 to 1.0
return p + (q - p) * 6 * t;
}
if(t < 1.0 / 2){
^^^ change from 1 to 1.0
return q;
}
if(t < 2.0 / 3){
^^^ change from 2 to 2.0
return p + (q - p) * ((2.0 / 3) - t) * 6;
^^^ change from 2 to 2.0
}
Also:
r = hueToRgb(p, q, h + (1.0 / 3));
^^^ change from 1 to 1.0
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - (1.0 / 3));
^^^ change from 1 to 1.0
Finally, in the code you copied from, the values for h, s, and l are all supposed to be between 0 and 1. So an H of 214 is not valid in your examples - you need to divide it by 360 first.
Here is a working code sample with the changes applied: http://ideone.com/QbXyYi
Input: 240.0 / 360.0, 1.0, 0.5
Output: [0, 0, 255.0]
Related
I was thinking about adding a while loop to the program that'll loop for maybe 10 times instead of writing 10 println statements. but I found it hard since the value of x is different every time in the math section. I wrote this code before, now I want to shorten it. It's a square root finder program that uses the Babylonian method to find the square root of an integer between [S > 20 || S < 400]
int S;
System.out.print("Enter an integer, S: ");
S = myInput.nextInt();
if (S < 0) {
System.out.println("This program can not take the square root of a negative number.");
}
else if (S < 20 || S > 400) {
System.out.println("This value is out of range.");
}
else {
double a = S / 2.0;
double b = S / a;
double c = a + b;
double d = 0.5 * c;
// for x2
double e = S / d;
double f = d + e;
double g = 0.5 * f;
// for x3
double h = S / g;
double i = g + h;
double j = 0.5 * i;
// for x4
double k = S / j;
double l = j + k;
double m = 0.5 * l;
// for x5
double n = S / m;
double o = m + n;
double p = 0.5 * o;
// for x6
double q = S / p;
double r = p + q;
double s = 0.5 * r;
// for x7
double t = S / s;
double u = s + t;
double v = 0.5 * u;
// for x8
double w = S / v;
double x = v + w;
double y = 0.5 * x;
// for x9
double z = S / y;
double aa = y + z;
double ab = 0.5 * aa;
System.out.printf("%nx0 = %8.4f ", a);
System.out.printf("%nx1 = %8.4f ", d);
System.out.printf("%nx2 = %8.4f ", g);
System.out.printf("%nx3 = %8.4f ", j);
System.out.printf("%nx4 = %8.4f ", m);
System.out.printf("%nx5 = %8.4f ", p);
System.out.printf("%nx6 = %8.4f ", s);
System.out.printf("%nx7 = %8.4f ", v);
System.out.printf("%nx8 = %8.4f ", y);
System.out.printf("%nx9 = %8.4f ", ab);
}
All you need to do is set d to 2.0 outside the loop. Then use d in place of 2.0 inside the loop. The loop index of i is also used to name the iterations (x0, x1, x2, ...) when printing.
double d = 2.0; // set d to 2.0 here
for (int i = 0; i < 10; i++) {
double a = S / d; // use d here, the modified value will be used again
double b = S / a;
double c = a + b;
d = 0.5 * c;
System.out.printf("x%d = %8.4f%n", i, a);
}
prints the following for the input value of 50
x0 = 25.0000
x1 = 3.7037
x2 = 5.8127
x3 = 6.9374
x4 = 7.0698
x5 = 7.0711
x6 = 7.0711
x7 = 7.0711
x8 = 7.0711
x9 = 7.0711
Here is an exercise. Compare the current computed value to the last. If they are equal (or their difference is small), then you can exit the loop since repeated iterations won't improve the accuracy very much.
This converter assumes H, S, and L are contained in the set [0, 1].
But I want H to be between [0, 359] (0 and 359 included) and S & L between [0, 100] (0 and 100 included) and I don't know how to do it (in Java as well).
I took this converter from here.
public class testdos {
public static void main(String[] args) {
// hslToRgb([0, 359], [0, 100], [0, 100]);
}
public static int[] hslToRgb(float h, float s, float l) {
// i added this sysout line
System.out.println("HSL(" + h + "," + s + "," + l + ")");
float r, g, b;
if (s == 0f) {
r = g = b = l; // achromatic
} else {
float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
float p = 2 * l - q;
r = hueToRgb(p, q, h + 1f / 3f);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1f / 3f);
}
int[] rgb = { to255(r), to255(g), to255(b) };
// and i added this to print it, don't know if its the best way to do it
System.out.print("RGB(");
for (int i = 0; i < rgb.length; i++) {
if ((i + 1) != rgb.length) {
System.out.print(rgb[i] + ",");
}
else {
System.out.print(rgb[i]);
}
}
System.out.println(")");
return rgb;
}
public static int to255(float v) {
return (int) Math.min(255, 256 * v);
}
/** Helper method that converts hue to rgb */
public static float hueToRgb(float p, float q, float t) {
if (t < 0f)
t += 1f;
if (t > 1f)
t -= 1f;
if (t < 1f / 6f)
return p + (q - p) * 6f * t;
if (t < 1f / 2f)
return q;
if (t < 2f / 3f)
return p + (q - p) * (2f / 3f - t) * 6f;
return p;
}
}
The hslToRgb() method you provided accepts three parameters of float data type. The value range that must be provided to all of these parameters is: 0.0f to 1.0f:
int[] hslToRgb(float h, float s, float l) { ... }
Instead you want to supply a specific range values to these parameters which in turn makes the method much easier to utilize in layman's terms with respect to what needs to actually be supplied.
H (0° to 360° as in Degrees)
S (0% to 100% as in Percent)
L (0% to 100% as in Percent)
It's all a matter of doing a little division. This can be done as the parameters are supplied to the method or built into the method which appears to be what you want. The latter is somewhat more advantageous since you don't need to remember the formula (regardless of how simple it is) to carry out those divisions when applying the arguments to the hslToRgb() method.
Doing the math before passing arguments:
// Hue is: 65° Saturation is: 36% Lightness is: 87%
float hue = (float) 65/360;
float saturation = (float) 36/100;
float lightness = (float) 87/100;
int[] rgb = hslToRgb(hue, saturation, lightness);
Doing the math when passing arguments:
// Hue is: 65° Saturation is: 36% Lightness is: 87%
int[] rgb = hslToRgb((float)65/360, (float)36/100, (float)87/100);
Who cares! Let the method figure it out:
// Hue is: 65° Saturation is: 36% Lightness is: 87%
int[] rgb = hslToRgb(65, 36, 87);
This last example requires a little bit of code to be added to the hslToRgb() method. It's nothing major but before we get into that you need to be sure to realize that for color accuracy, Hue, Saturation, and Lightness arguments can be given respectively in floating point degree and floating point percentage values. In other words, supplying arguments such as:
int[] rgb = hslToRgb(65, 36, 87);
AND
int[] rgb = hslToRgb(65.8, 36.2, 87.5);
are to be considered valid calls with valid arguments. With the slight code addition to the hslToRgb() method, either or can be supplied, for example:
int[] rgb = hslToRgb(0.1806f, 36, 87.5);
This is a valid call with valid arguments and will produce the very same RGB values as the other above examples.
Modifying the hslToRgb() method:
Simply add these three lines of code to the top of the hslToRGB() method:
public static int[] hslToRGB(float h, float s, float l) {
if (h > 1.0f) { h = (h < 0 ? 0 : h > 360 ? 360 : h) / 360; }
if (s > 1.0f) { s = (s < 0 ? 0 : s > 100 ? 100 : s) / 100; }
if (l > 1.0f) { l = (l < 0 ? 0 : l > 100 ? 100 : l) / 100; }
// .... the rest of method code here ....
}
What are these three lines of code doing?
With these lines of code the Hue (h) argument can be supplied as a set [0.0 to 1.0] or a set [0.0 to 360.0] or a set [0 to 360]. The Saturation (s) and Lightness (l) arguments can be supplied as a set [0.0 to 1.0] or a set [0.0 to 100.0] or a set [0 to 100]. If any argument supplied is less than its minimum range (0) then it is defaulted to that arguments specific minimum which for all arguments would be 0. On the same hand, if any argument supplied is greater than its maximum range then it is defaulted to that arguments specific maximum.
Since all three lines of code are basically doing the same thing for each specific argument, we'll explain the first line:
if (h > 1.0f) { h = (h < 0 ? 0 : h > 360 ? 360 : h) / 360; }
The condition for the if statement (h > 1.0f) checks to see if the supplied Hue (h) argument is within the set of [0.0 to 1.0]. If the argument supplied is greater than 1.0 then it must be supplied as a literal degree value and the if statement condition is true therefore running the code contained within this statement's code block which consists of nested Ternary Operator statements. If the if statement condition is false then the supplied value is merely used. Ultimately, if the condition is true then we want to take the supplied argument and divide it by 360 but first we use the nested Ternary Operator statements to ensure the argument supplied is within valid range (0 to 360):
h < 0 ? 0 :
If the supplied argument (h) is less than 0 (such as an arbitrary value of -22 or -0.202) then the h is set to hold a default of 0 otherwise we check to see if the argument value is greater then 360:
: h > 360 ? 360 :
If the supplied argument (h) is greater than 360 (such as an arbitrary value of 365 or 378.8) then the h is set to hold a default of 360 otherwise it is deemed that the argument supplied is indeed within the proper range and we'll use it therefore dividing the supplied value by 360;
: h) / 360
How you might use this method:
int[] rgb = hslToRGB(65, 36, 87);
System.out.println("RGB is: " + java.util.Arrays.toString(rgb));
String hex = "#" + Integer.toHexString(new Color(rgb[0], rgb[1], rgb[2]).getRGB() & 0x00ffffff);
System.out.println("In Hexadecimal: " + hex.toUpperCase());
Console Output:
RGB is: [232, 234, 210]
In Hexadecimal: #E8EAD2
You divide by the h, s, and l range to get the numbers between 0 and 1. You also start class names with an upper-case character.
Edited to add: Why do I have to start class names with an upper-case character?
Because it's the Java convention, and so you can write code like:
Testdoc testdoc = new TestDoc();
and visually see the difference between the Testdoc class name and the testdoc field name.
Here's the revised code.
public class Testdos {
public static void main(String[] args) {
// hslToRgb([0, 359], [0, 100], [0, 100]);
}
/**
* Converts an HSL color value to RGB. Conversion formula adapted from
* http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are
* contained in the set [0, 1] and returns r, g, and b in the set [0, 255].
*
* #param h The hue
* #param s The saturation
* #param l The lightness
* #return int array, the RGB representation
*/
public static int[] hslToRgb(float h, float s, float l) {
// i added this sysout line
System.out.println("HSL(" + h + "," + s + "," + l + ")");
h /= 359.0;
s /= 100.0;
l /= 100.0;
float r, g, b;
if (s == 0f) {
r = g = b = l; // achromatic
} else {
float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
float p = 2 * l - q;
r = hueToRgb(p, q, h + 1f / 3f);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1f / 3f);
}
int[] rgb = { to255(r), to255(g), to255(b) };
// and i added this to print it, don't know if its the best way to do it
System.out.print("RGB(");
for (int i = 0; i < rgb.length; i++) {
if ((i + 1) != rgb.length) {
System.out.print(rgb[i] + ",");
} else {
System.out.print(rgb[i]);
}
}
System.out.println(")");
return rgb;
}
public static int to255(float v) {
return (int) Math.min(255, 256 * v);
}
/** Helper method that converts hue to rgb */
public static float hueToRgb(float p, float q, float t) {
if (t < 0f)
t += 1f;
if (t > 1f)
t -= 1f;
if (t < 1f / 6f)
return p + (q - p) * 6f * t;
if (t < 1f / 2f)
return q;
if (t < 2f / 3f)
return p + (q - p) * (2f / 3f - t) * 6f;
return p;
}
}
In an STL format this is what my current landscape looks like. And this is what the landscape is suppossed to look like. I think I know what the problem is, but I have no clue how to solve it.
I think I need to set the Z coordinate relative to other points around it so the whole landscape's Z coordinate isn't between 0 and 1 but they rather "add up".
Don't want a solution, just a hint in the right direction.
import java.io.*;
import java.util.Random;
class Point3D {
double x, y, z;
Point3D(double dx, double dy, double dz) {
x = dx;
y = dy;
z = dz;
}
Point3D middlePoint(Point3D p) {
Point3D m = new Point3D(0.0, 0.0, 0.0);
m.x = (this.x + p.x) / 2.0;
m.y = (this.y + p.y) / 2.0;
m.z = (this.z + p.z) / 2.0;
return m;
}
}
public class Aufgabe3 {
public static void recursion(Point3D p1, Point3D p2, Point3D p3, int n) {
if (n > 0) {
if (n == 1) {
System.out.println(" facet normal 0.0 0.0 0.0");
System.out.println(" outer loop");
System.out.println(" vertex " + p1.x + " " + p1.y + " " + p1.z);
System.out.println(" vertex " + p2.x + " " + p2.y + " " + p2.z);
System.out.println(" vertex " + p3.x + " " + p3.y + " " + p3.z);
System.out.println(" endloop");
System.out.println(" endfacet");
}
Random r = new Random();
Point3D a = p1.middlePoint(p2);
Point3D b = p3.middlePoint(p1);
Point3D c = p2.middlePoint(p3);
long seedA = (long) ((p1.x + p1.y + p1.z + p2.x + p2.y + p2.z) * 1000000);
r.setSeed(seedA);
a.z = r.nextDouble() / 10;
long seedB = (long) ((p3.x + p3.y + p3.z + p1.x + p1.y + p1.z) * 1000000);
r.setSeed(seedB);
b.z = r.nextDouble() / 10;
long seedC = (long) ((p2.x + p2.y + p2.z + p3.x + p3.y + p3.z) * 1000000);
r.setSeed(seedC);
c.z = r.nextDouble() / 10;
recursion(p1, a, b, n-1);
recursion(a, p2, c, n-1);
recursion(b, c, p3, n-1);
recursion(a, b, c, n-1);
}
}
public static void main(String args[]) throws FileNotFoundException {
int n;
try {
n = Integer.parseInt(args[0]);
}
catch (Exception e) {
n = 7;
}
System.out.println("Aufgabe 3: Landschaftsgenerator");
System.out.println("n = " + n);
Random r = new Random();
Point3D p1 = new Point3D(0.8, -1.2, 0.0);
Point3D p2 = new Point3D(1.0, 1.3, 0.0);
Point3D p3 = new Point3D(-1.0, 0.0, 0.0);
System.setOut(new PrintStream(new FileOutputStream("Aufgabe3.stl")));
System.out.println("solid Aufgabe3");
recursion(p1, p2, p3, n);
System.out.println("endsolid");
}
}
The problem is the frequency distribution of the displacements you're adding. Displacements to a fractal landscape have to follow a 1/(f^b) distribution, otherwise you get random noise.
In this case, no matter what the scale of subdivision, you're adding the same vertical displacement, which is going to result in a landscape dominated by the highest frequency. Formally, a fractal surface is one that has a 'fractional' or 'fractal' geometric dimension, higher than the topological dimension of the surface, but lower than that of the embedding space. For instance, for a 2D surface being displaced in the 3rd dimension, the fractal dimension should be between 2 and 3.
For subdivision and displacement, the fractal dimension is related to beta as follows:
Dim = (7 - b)/2
With fractal behaviour therefore occurring between b = 1 and b = 3, and the random displacements follow this profile:
displacement = k * rand / (f^b)
This means that if you divide your triangle in half each time, you have to at least halve the displacement, or you'll end up with a noise surface rather than a fractal one. The best choice for a landscape is typically somewhere around b = 2.
Reference: https://fractal-landscapes.co.uk/maths.html
I need to convert the following python code to Java and remember from the past that handling decimal values must be done carefully otherwise the results will not be accurate.
My question is, can I use doubles for all non-interger values given doubles are more accurate that floats?
Here is my start of the conversion:
Python code
def degrees(rads):
return (rads/pi)*180
def radians(degrees):
return (degrees/180 * pi)
def fnday(y, m, d, h):
a = 367 * y - 7 * (y + (m + 9) // 12) // 4 + 275 * m // 9
a += d - 730530 + h / 24
return a
Java conversion
public double degress(double rads)
{
return (rads/PI)*180;
}
public double radians(double degrees)
{
return (degrees/180 * PI);
}
public double fnday(int y, int m, int d, int h)
{
double a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9;
a += d - 730530 + h / 24;
return a;
}
I know it may be a simple answer but I need to know the postion of the moon and sun for the app and do not want to rely on an api for this data. I simple want to put in the latitude and longitdue and get the sun and moon rise and set times.
Using a double for each variable would suffice; however, you have an issue that results from integer division:
double a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9;
If I were you, I'd change y, m, d, and h to all be doubles, so you retain the decimal places when dividing:
public double fnday(double y, double m, double d, double h) {
double a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9;
a += d - 730530 + h / 24;
return a;
}
If you need a really big precision, the best way is use,
java.lang.BigDecimal, that extends from java.math.Number.
You can even use your existing doubles if you need:
double d = 67.67;
BigDecimal bd = new BigDecimal(d);
But you will need to use the methods from the class like this:
public BigDecimal degress(BigDecimal rads)
{
BigDecimal pi = new BigDecimal(PI);
return (rads.divide(pi))*180;
}
I'm attempting to follow some psuedo code for solving cubic equations in Mathematics and Physics for Programmers, Chapter 3, as far as I can see I've followed it accurately, but I don't seem to be getting correct outputs.
For example: According to Wolfram Alpha 5x^3 + 4x^2 + 3x + 2 = 0 should give a root of x≈-0.72932, but I'm getting back -1.8580943294965526 from my script.
Can someone help me to work out what the hell I'm doing? I'm following the scripts to get a better understanding of maths and converting equations to code. But this is at the brink of my understanding so I'm finding it troublesome to debug. Coupled with the fact the book has no errata and many online reviews state the book has many errors, I'm struggling to see whether the issue is with my code, the books explanation or both.
The equation given in the book is:
Then if the discriminant > 0 then root has value of r+s:
if discriminant == 0 then there are two roots:
if discriminant < 0 you can find three roots as follows:
After finding t you can transform it to x by taking:
package com.oacc.maths;
public class SolveCubic {
public static double[] solveCubic(double a, double b, double c, double d) {
double[] result;
if (d != 1) {
a = a / d;
b = b / d;
c = c / d;
}
double p = b / 3 - a * a / 9;
double q = a * a * a / 27 - a * b / 6 + c / 2;
double D = p * p * p + q * q;
if (Double.compare(D, 0) >= 0) {
if (Double.compare(D, 0) == 0) {
double r = Math.cbrt(-q);
result = new double[2];
result[0] = 2 * r;
result[1] = -r;
} else {
double r = Math.cbrt(-q + Math.sqrt(D));
double s = Math.cbrt(-q - Math.sqrt(D));
result = new double[1];
result[0] = r + s;
}
} else {
double ang = Math.acos(-q / Math.sqrt(-p * p * p));
double r = 2 * Math.sqrt(-p);
result = new double[3];
for (int k = -1; k <= 1; k++) {
double theta = (ang - 2 * Math.PI * k) / 3;
result[k + 1] = r * Math.cos(theta);
}
}
for (int i = 0; i < result.length; i++) {
result[i] = result[i] - a / 3;
}
return result;
}
public static double[] solveCubic(double a, double b, double c) {
double d = 1;
return solveCubic(a, b, c, d);
}
public static void main(String args[]) {
double[] result = solveCubic(5, 4, 3, 2);
for (double aResult : result) {
System.out.println(aResult);
}
}
}
I also found this code example from the book site(n.b. this is not the psuedo code from the book): http://www.delmarlearning.com/companions/content/1435457331/files/index.asp?isbn=1435457331
on solveCubic(a,b,c,d)
--! ARGUMENTS:
a, b, c, d (all numbers). d is optional (default is 1)
--!
RETURNS: the list of solutions to dx^3+ax^2+bx+c=0
-- if d is defined then divide a, b and c by d
if not voidp(d)
then
if d=0 then return solveQuadratic(b,c,a)
set d to float(d)
set a to a/d
set b to b/d
set
c to c/d
else
set a to float(a)
set b to float(b)
set c to float(c)
end if
set p to b/3 - a*a/9
set q to a*a*a/27 - a*b/6 + c/2
set disc to p*p*p + q*q
if abs(disc)<0.001 then
set r to cuberoot(-q)
set ret to [2*r, -r]
else if disc>0 then
set r to cuberoot(-q+sqrt(disc))
set s to cuberoot(-q-sqrt(disc))
set ret to [r+s]
else
set ang to acos(-q/sqrt(-p*p*p))
set r to 2*sqrt(-p)
set ret to []
repeat with k=-1 to 1
set theta
to (ang - 2*pi*k)/3
ret.add(r*cos(theta))
end repeat
end if
set ret to ret-a/3 --NB: this adds the value to each
element
return ret
end
The error appears to be with the names of the parameters of your solveCubic method.
It seems your book is explaining how to solve the equation x3 + ax2 + bx + c = 0. You are calling your method thinking that the parameters a, b, c and d are for the equation ax3 + bx2 + cx + d = 0. However, it turns out that the body of your method is actually finding solutions to the equation dx3 + ax2 + bx + c = 0.
Aside from this naming error, the calculations appear to be correct. Try plugging your value ≈-1.858 into 2x3 + 5x2 + 4x + 3 if you don't believe me.
If you instead declare your solveCubic method as
public static double[] solveCubic(double d, double a, double b, double c) {
the parameters then correspond to the equation dx3 + ax2 + bx + c. You should then find that your method gives you the answer you expect.
Okay. So, first off the equations from the book seem to be referring to this idea: If you have an equation of the form:
Then by defining t as x - (a/3) you can transform this into an equation with no quadratic term, as verified by a bit of Mathematica:
Once you have no quadratic term, you can then apply the method given; let q be half the constant term, let p be one third the coefficient on the first power term, and define D (discriminant) as p*p*p - q*q.
All else then follows.
So why does your code not work? Because you've mislabeled the variables. a is the coefficient on x^2, not on x^3. If you call your method with the arguments (0.8, 0.6, 0.4, 1) or equivalently with (4, 3, 2, 5), you'll get the right answer.
(Or do as the other answer suggests and more around the variable names in the method declaration)
This works 100%. It has been tried and tested for all combinations.
public void solveCubicEquation(int A, int B, int C, int D) {
double a = (double) B / A;
double b = (double) C / A;
double c = (double) D / A;
System.out.println("Double values: ");
System.out.println(a + " " + b + " " + c);
double p = b - ((a * a) / 3.0);
double q = (2 * Math.pow(a, 3) / 27.0) - (a * b / 3.0) + c;
double delta = (Math.pow(q, 2) / 4) + (Math.pow(p, 3) / 27);
if (delta > 0.001) {
double mt1, mt2;
double t1 = (-q / 2.0) + Math.sqrt(delta);
double t2 = (-q / 2.0) - Math.sqrt(delta);
if (t1 < 0) {
mt1 = (-1) * (Math.pow(-t1, (double) 1 / 3));
} else {
mt1 = (Math.pow(t1, (double) 1 / 3));
}
if (t2 < 0) {
mt2 = (-1) * (Math.pow(-t2, (double) 1 / 3));
} else {
mt2 = (Math.pow(t2, (double) 1 / 3));
}
x1 = mt1 + mt2 - (a / 3.0);
} else if (delta < 0.001 && delta > -0.001) {
if (q < 0) {
x1 = 2 * Math.pow(-q / 2, (double) 1 / 3) - (a / 3);
x2 = -1 * Math.pow(-q / 2, (double) 1 / 3) - (a / 3);
} else {
x1 = -2 * Math.pow(q / 2, (double) 1 / 3) - (a / 3);
x2 = Math.pow(q / 2, (double) 1 / 3) - (a / 3);
}
} else {
System.out.println("here");
x1 = (2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.sin((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))))) - (a / 3.0);
x2 = (-2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.sin((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))) + (Math.PI / 3))) - (a / 3.0);
x3 = (2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.cos((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))) + (Math.PI / 6))) - (a / 3.0);
}
}
Note: will not work for imaginary values