implementation formula volume of a sphere with Java and JUnit? - java

I have to make an implementation to calculate volume of a sphere to be checked with JUnit test, but there are some errors. The formula is correct, but when I test it, it doesn't work :
class VolumeSphere.java
public class VolumeSphere {
public static double volsph(double j) {
double volume;
double const = 1.33;
double phi = 3.14;
volume = const * phi * (j * j * j);
return volume;
}
}
and then this the test file :
VolumeSphereTest.java
import junit.framework.*;
public class VolumeSphereTest extends TestCase {
public VolumeSphereTest(String name) {
super(name);
}
public void testSimple() {
assertEquals(33.4096, VolumeSphere.volsph(2.0));
}
}
when I run the JUnit test, it's said "Expected: (33.4096) but was: (33.4096000005)."
So, what should I do? Thankyou in advance for the help!

The problem is that 33.4096 isn't exactly represented by a double, nor is 1.33, and nor is 3.14. Moreover, the multiplication introduces its own errors. Therefore, the assertEquals needs to be replaced by something that basically means "assert that the value is very close to what we expect".
JUnit has assertEquals(expectedValue, actualValue, errorPermitted) for comparing doubles, which is what you should use here.
In general, double is a poor choice of data type for doing exact arithmetic with numbers expressed as decimals, because it stores binary representations of numbers. If you want accuracy with exact decimals, use BigDecimal instead.

const is a keyword and can't be a name of a variable - pick a different name for your variable.

The problem is that the answer isn't exactly the value you let the JUnit test compare to. The answer is 33.409600000000005 instead of 33.4096. To remedy this, you could use assertEquals(33.4096, VolumeSphere.volsph(2.0), 0.0001);.
This will allow all answers within a difference of 0.0001 around 33.4096. Therefor in this case it will allow 33.4095 to 33.4097.
Also, instead of using double phi = 3.14, you could use Math.PI, which inserts the more significant value of constant pi.

Related

Issue with using array in a class Java

I am trying to build a Java program based on this UML:
UML of Polygon Class
But I ran into a few hiccups along the way. This is my basic code:
import java.util.Scanner;
public class Polygon {
private int[] side;
private double perimeter;
public double addSide(double length[]) {
int i = 0;
double perimeter = 0;
while(length[i] > 0){
perimeter += (double)length[i];
i++;
}
return perimeter;
}
public int[] getSides() {return side;}
public double getPerimeter() {return perimeter;}
public static void main(String[] args) {
Polygon polygon=new Polygon();
polygon.side = new int[99];
int i=0;
do{
System.out.print("Side length(0 when done): ");
Scanner in = new Scanner(System.in);
polygon.side[i] = in.nextInt();
i++;
}while(polygon.side[i]>0);
//polygon.perimeter = addSide((double)polygon.side);
System.out.println("Perimeter of " + i + "-sided polygon: " + polygon.getPerimeter());
}
}
There's a couple of issues.
I got it to compile but when it accepts the first side[0], it immediately stops and gives me the perimeter. Exiting the loop eventhough the conditions haven't been met for it to so. So there's an issue with my while-loop. I want it to keep accepting values into the side[] array until a non-positive value is entered.
Also the UML requires I use double parameter-type for the addSide method. I tried to cast it in the argument and tried a couple of other different things with no success. How would one transition an int-array into a double-array for the perimeter calucalation which has to be double as per the requirements.
I wouldn't surprised if I made other issues since I'm new to Java so feel free to point them out to me or if you have a better way to go about this, I would love to learn your thinking.
Any advice is appreciated!
There are a number of issues with your code.
First, differences from the UML specification:
You haven't used the given signature for addSide. The UML says that it takes a single double parameter, and returns nothing, i.e. void in Java. You are passing an array of double and returning a double.
You are directly accessing sides in your main method. Java allows you to do this, because your main method is part of the Polygon class, but the UML shows that the field is private. What does direct manipulation of sides do to the validity of the value in perimeter?
The UML shows the class having a field sides of type int. Your field sides is of type int[].
Similarly you haven't used the given signature for getSides, which should probably have been named getNumberOfSides.
Your code has quite a few other issues, but I think you should fix the issues above first.
A futher hint: The only things that the Polygon class can do is to tell you how many sides it has and what its total perimeter is. It does not care about the details of individual sides.
(Off topic, it is strange to include main in the UML description of Polygon)

Why is assertEquals(double,double) deprecated in JUnit?

I was wondering why assertEquals(double, double) is deprecated.
I used import static org.junit.Assert.assertEquals; and I used JUnit 4.11.
Below is my code:
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class AccountTest {
#Test
public void test() {
Account checking = new Account(Account.CHECKING);
checking.deposit(1000.0);
checking.withdraw(100.0);
assertEquals(900.0, checking.getBalance());
}
}
checking.getBalance() returns a double value.
What could be wrong?
It's deprecated because of the double's precision problems.
If you note, there's another method assertEquals(double expected, double actual, double delta) which allows a delta precision loss.
JavaDoc:
Asserts that two doubles are equal to within a positive delta. If they are not, an AssertionError is thrown. If the expected value is infinity then the delta value is ignored.NaNs are considered equal: assertEquals(Double.NaN, Double.NaN, *) passes
...
delta - the maximum delta between expected and actual for which both numbers are still considered equal.
People explain but don't give samples... So here goes what worked for me:
#Test
public void WhenMakingDepositAccountBalanceIncreases() {
Account account = new Account();
account.makeDeposit(10.0);
assertEquals("Account balance was not correct.", 10.0, account.getBalance(), 0);
}
The 0 in the end;
assertEquals(double, double) is deprecated because the 2 doubles may be the same but if they are calculated values, the processor may make them slightly different values.
If you try this, it will fail: assertEquals(.1 + .7, .8). This was tested using an Intel® processor.
Calling the deprecated method will trigger fail("Use assertEquals(expected, actual, delta) to compare floating-point numbers"); to be called.
Old question but this hasn't been said yet and might help someone.
You can use com.google.common.math.DoubleMath.fuzzyEquals(double a, double b, double tolerance) which allows you to specify how close the two doubles should be to each other.
I found it very handy for unit tests where I don't want to hardcode test result values with a lot of decimal places.

Wrong rounding with float division

I was doing some arithmetic program today and I got a real funny result passing the result of float division to a setter :
class A {
Float f;
setF(Float f) {
this.f=f;
print (f)
}
}
Long x=7L;
Long y=3L;
print (x/y.floatValue() )
a.setF(x/y.floatValue());
the result of the above pseudo program is something like this in the jdk 1.6
2.333333
2.0
any clue on where the round is performed?
Let me explain your first condition i.e. `x/y.floatValue()' what you are doing is:
long/ float because you are taking float value from variable y from floatValue() method so according to its implementation, you will get y = 3.0F.
/**
* Returns the value of this {#code Long} as a
* {#code float}.
*/
public float floatValue() {
return (float)value;
}
Your division will be 7L/ 3F or say 7/ 3.0 which will give you the result as 2.333333
Your second condition a.setF(x/y.floatValue()); will also result 2.333333 check if you missed something or post your whole code.
As you can see in console header I am using JDK1.6
It's a casting issue. Your x is still type long, whereas y.floatValue() is a float.
Both the divisor and the dividend need to be float or double to get what you want, so try for example, this:
Long x=7L;
Long y=3L;
System.out.println((float) x/(float) y);
result -> 2.3333333
This rounding down to 2.0 does not happen, there may be a problem with how you wrote the code from that pseudo-code
I tested with this implementation
public class A {
Float f;
void setF(Float f){
this.f=f;
System.out.println(f);
}
public static void main(String[] args) {
Long x=7L;
Long y=3L;
System.out.println(x/y.floatValue());
new A().setF(x/y.floatValue());
}
}
This is the result
2.3333333
2.3333333

call method that rounds java

OK so I can't understand why it says the method isn't being used locally.... The private String formatNumber() method is saying this.
Basically what I need to do is have a method that returns the circumference
- another method that rounds numbers to 2 decimal places and returns a string
- and another method that returns the formatted version of circumference...
It's not hard to see what I'm trying to do, but it gives me the above stated error and I can't figure it out.
//figures out circumference
public double getCircumference(){
circumference = 2 * Math.PI * radius;
return circumference;
}
//takes string and turns back into a double
public double getFormattedCircumference(){
double x = Double.parseDouble(format);
return x;
}
//this method is giving the error of not being used locally...
//method takes double and turns to string so that it can be formatted and it
has to be a string
private String formatNumber(double x){
x = circumference;
NumberFormat number = NumberFormat.getNumberInstance();
number.setMaximumFractionDigits(2);
String format = number.format(x);
return format;
}
You've declared the private method but you've not used it in your current code anywhere and so the compiler is warning you of this (check your program to see if you're calling this method anywhere).
Incidentally, what you're seeing is a warning not an error. Your code should still compile, and the program will still run (if there are no errors present).
Edit 1
You've a serious problem with the method, and maybe more than one, in that it takes in a double parameter and then promptly discards it. Why? If you want to format the number that is passed in as a parameter, then you don't want to discard that parameter. Also, do you want to make this method public so that it can be called by objects outside of this class? Also, will the method have state or will it be stateless? Will it use the fields of the class, or will it only format the number passed into it. If the latter, than it should be a static method.
I got it all figured out. I was making it harder than it actually was.
//figures out circumference
public double getCircumference(){
circumference = 2 * Math.PI * radius;
return circumference;
}
public String getFormattedCircumference(){
return formatNumber(getCircumference());
}
//formats to two decimal places.
private String formatNumber(double x){
NumberFormat number = NumberFormat.getNumberInstance();
number.setMaximumFractionDigits(2);
String format = number.format(x);
return format;
}

Using BigDecimal to work with currencies

I was trying to make my own class for currencies using longs, but apparently I should use BigDecimal instead. Could someone help me get started? What would be the best way to use BigDecimals for dollar currencies, like making it at least but no more than 2 decimal places for the cents, etc. The API for BigDecimal is huge, and I don't know which methods to use. Also, BigDecimal has better precision, but isn't that all lost if it passes through a double? if I do new BigDecimal(24.99), how will it be different than using a double? Or should I use the constructor that uses a String instead?
Here are a few hints:
Use BigDecimal for computations if you need the precision that it offers (Money values often need this).
Use the NumberFormat class for display. This class will take care of localization issues for amounts in different currencies. However, it will take in only primitives; therefore, if you can accept the small change in accuracy due to transformation to a double, you could use this class.
When using the NumberFormat class, use the scale() method on the BigDecimal instance to set the precision and the rounding method.
PS: In case you were wondering, BigDecimal is always better than double, when you have to represent money values in Java.
PPS:
Creating BigDecimal instances
This is fairly simple since BigDecimal provides constructors to take in primitive values, and String objects. You could use those, preferably the one taking the String object. For example,
BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
Displaying BigDecimal instances
You could use the setMinimumFractionDigits and setMaximumFractionDigits method calls to restrict the amount of data being displayed.
NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
I would recommend a little research on Money Pattern. Martin Fowler in his book Analysis pattern has covered this in more detail.
public class Money {
private static final Currency USD = Currency.getInstance("USD");
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
private final BigDecimal amount;
private final Currency currency;
public static Money dollars(BigDecimal amount) {
return new Money(amount, USD);
}
Money(BigDecimal amount, Currency currency) {
this(amount, currency, DEFAULT_ROUNDING);
}
Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
}
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
#Override
public String toString() {
return getCurrency().getSymbol() + " " + getAmount();
}
public String toString(Locale locale) {
return getCurrency().getSymbol(locale) + " " + getAmount();
}
}
Coming to the usage:
You would represent all monies using Money object as opposed to BigDecimal. Representing money as big decimal will mean that you will have the to format the money every where you display it. Just imagine if the display standard changes. You will have to make the edits all over the place. Instead using the Money pattern you centralize the formatting of money to a single location.
Money price = Money.dollars(38.28);
System.out.println(price);
Or, wait for JSR-354. Java Money and Currency API coming soon!
1) If you are limited to the double precision, one reason to use BigDecimals is to realize operations with the BigDecimals created from the doubles.
2) The BigDecimal consists of an arbitrary precision integer unscaled value and a non-negative 32-bit integer scale, while the double wraps a value of the primitive type double in an object. An object of type Double contains a single field whose type is double
3) It should make no difference
You should have no difficulties with the $ and precision. One way to do it is using System.out.printf
Use BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP) when you want to round up to the 2 decimal points for cents. Be aware of rounding off error when you do calculations though. You need to be consistent when you will be doing the rounding of money value. Either do the rounding right at the end just once after all calculations are done, or apply rounding to each value before doing any calculations. Which one to use would depend on your business requirement, but generally, I think doing rounding right at the end seems to make a better sense to me.
Use a String when you construct BigDecimal for money value. If you use double, it will have a trailing floating point values at the end. This is due to computer architecture regarding how double/float values are represented in binary format.
Primitive numeric types are useful for storing single values in memory. But when dealing with calculation using double and float types, there is a problems with the rounding.It happens because memory representation doesn't map exactly to the value. For example, a double value is supposed to take 64 bits but Java doesn't use all 64 bits.It only stores what it thinks the important parts of the number. So you can arrive to the wrong values when you adding values together of the float or double type.
Please see a short clip https://youtu.be/EXxUSz9x7BM
I would be radical. No BigDecimal.
Here is a great article
https://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/
Ideas from here.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
testConstructors();
testEqualsAndCompare();
testArithmetic();
}
private static void testEqualsAndCompare() {
final BigDecimal zero = new BigDecimal("0.0");
final BigDecimal zerozero = new BigDecimal("0.00");
boolean zerosAreEqual = zero.equals(zerozero);
boolean zerosAreEqual2 = zerozero.equals(zero);
System.out.println("zerosAreEqual: " + zerosAreEqual + " " + zerosAreEqual2);
int zerosCompare = zero.compareTo(zerozero);
int zerosCompare2 = zerozero.compareTo(zero);
System.out.println("zerosCompare: " + zerosCompare + " " + zerosCompare2);
}
private static void testArithmetic() {
try {
BigDecimal value = new BigDecimal(1);
value = value.divide(new BigDecimal(3));
System.out.println(value);
} catch (ArithmeticException e) {
System.out.println("Failed to devide. " + e.getMessage());
}
}
private static void testConstructors() {
double doubleValue = 35.7;
BigDecimal fromDouble = new BigDecimal(doubleValue);
BigDecimal fromString = new BigDecimal("35.7");
boolean decimalsEqual = fromDouble.equals(fromString);
boolean decimalsEqual2 = fromString.equals(fromDouble);
System.out.println("From double: " + fromDouble);
System.out.println("decimalsEqual: " + decimalsEqual + " " + decimalsEqual2);
}
}
It prints
From double: 35.7000000000000028421709430404007434844970703125
decimalsEqual: false false
zerosAreEqual: false false
zerosCompare: 0 0
Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.
How about storing BigDecimal into a database? Hell, it also stores as a double value??? At least, if I use mongoDb without any advanced configuration it will store BigDecimal.TEN as 1E1.
Possible solutions?
I came with one - use String to store BigDecimal in Java as a String into the database. You have validation, for example #NotNull, #Min(10), etc... Then you can use a trigger on update or save to check if current string is a number you need. There are no triggers for mongo though.
Is there a built-in way for Mongodb trigger function calls?
There is one drawback I am having fun around - BigDecimal as String in Swagger defenition
I need to generate swagger, so our front-end team understands that I pass them a number presented as a String. DateTime for example presented as a String.
There is another cool solution I read in the article above...
Use long to store precise numbers.
A standard long value can store the current value of the Unites States national debt (as cents, not dollars) 6477 times without any overflow. Whats more: it’s an integer type, not a floating point. This makes it easier and accurate to work with, and a guaranteed behavior.
Update
https://stackoverflow.com/a/27978223/4587961
Maybe in the future MongoDb will add support for BigDecimal.
https://jira.mongodb.org/browse/SERVER-1393
3.3.8 seems to have this done.
It is an example of the second approach. Use scaling.
http://www.technology-ebay.de/the-teams/mobile-de/blog/mapping-bigdecimals-with-morphia-for-mongodb.html
There is an extensive example of how to do this on javapractices.com. See in particular the Money class, which is meant to make monetary calculations simpler than using BigDecimal directly.
The design of this Money class is intended to make expressions more natural. For example:
if ( amount.lt(hundred) ) {
cost = amount.times(price);
}
The WEB4J tool has a similar class, called Decimal, which is a bit more polished than the Money class.
NumberFormat.getNumberInstance(java.util.Locale.US).format(num);

Categories