Floating Point Numbers - java

This seems like a real simple question but I just to want clear my doubt. I am looking at code which some other developer wrote. There are some calculations involving floating-point numbers.
Example: Float fNotAvlbl = new Float(-99); Why is he creating a new object? What would happen if we do Float fNotAvlbl = -99;(-99 is used as flag here to indicate Not Applicable) Later down the code, we define:
fltValue1 = 0.00f;
fltValue2 = 0.00f;
and populate these two values with a method call which returns float. After that we again convert these two values into Float Objects with:
fltVal1 = new Float(fltValue1);
fltVal2 = new Float(fltValue2);
and than do a comparison if(fltVal1.compareTo(fNotAvailable) == 0) do something.
Is it all because compareTo expects Wrapper Class Objects?
I apologize if this is a real basic question.

Writing Float fNotAvlbl = -99; relies on autoboxing, which has only been added in Java 5, so older code could not use it.
Using -99 as a Float value to mean "Not Applicable" is really, really bad. Either use null or Float.Nan
fltVal1.compareTo(fNotAvailable) == 0 means exactly the same as fltValue1==fltValue2
Comparing float values for strict equality should not be done because it will often fail to work as expected. Read The Float-Point Guide to understand why.

You don't need the wrappers at all
Even if you needed them, using the constructor is not preferred - use Float.valueOf(..) instead.

On the subject of what compareTo() does compared with ==
float a = Float.NaN;
float b = Float.NaN;
System.out.println(a + " == " + b + " is " + (a == b));
System.out.println(a + ".compareTo(" + b + ") is " + ((Float) a).compareTo(b));
float c = -0.0f;
float d = 0.0f;
System.out.println(c + " == " + d + " is " + (c == d));
System.out.println(c + ".compareTo(" + d + ") is " + ((Float) c).compareTo(d));
prints
NaN == NaN is false
NaN.compareTo(NaN) is 0
-0.0 == 0.0 is true
-0.0.compareTo(0.0) is -1
compareTo compares the binary representation (after normalising all NaN values to be the same) As the binary representation for -0.0f and 0.0f are different compareTo does not return 0. There is no special handling in the code other that to use floatToIntBits() and compare that instea dof using ==

The comparison may be using the Float object rather than the built-in float type because of the inherent issues with floating point comparison. Because of the way that floating point numbers are stored on a computer system occasionally the equality comparison between two floating point numbers throws up false negatives. The compareTo from Float may take this into account. The original author possibly at least thought it did.
You can also write your own floating point comparison algorithm that checks for a difference within a reasonable standard deviation for your system. You can also use the method used by equals in Float, which looks at the integer value of the floating point bits for each number.
Note that using Float in and of itself doesn't fix this problem, as the problem is a round-off error in storage.

Related

Floating-point equality unexpectedly working

We are often taught that floating-point numbers should not be compared for exact equality. However, the following function, which returns the Golden Ratio when passed any positive number, does in fact compare doubles for equality and to my surprise it seems to always work:
public static double f(double x) {
double y;
while ((y = 1 + 1 / x) != x)
x = (x + y) / 2;
return x;
}
#Test
void test() {
assertEquals((1 + sqrt(5)) / 2, f(1.0)); // Passes!
}
I thought that maybe it works for some input arguments but not others. But even if I use JQwik's property testing, it still works!
#Property
void test2(#ForAll #Positive double x) {
assertEquals((1 + sqrt(5)) / 2, f(x)); // Always passes!
}
Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?
You were just lucky, in general you don't get exact equality. Try this for example:
public static void main(String[] args) {
var s = 0.0;
for (int i = 0; i < 10; i++) {
s += 0.1;
}
System.out.println(s == 1.0);
}
In your concrete example one would have to do a careful analysis to prove that your iteration always converges to the floating point number closest to phi. If sqrt also returns the closest floating point number to the exact root we would get exact equality.
... and to my surprise it seems to always work:
Not always.
When I tried f(-1/1.6180339887498949), the x and y values oscillated between two floating point values that differed in the last few bits #Steve Summit. Thus an infinite loop.
x:-0.61803398874989490 y:-0.61803398874989468 // Decimal notation
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1 // Hex notation
x:-0.61803398874989479 y:-0.6180339887498949
x:-0x1.3c6ef372fe94fp-1 y:-0x1.3c6ef372fe950p-1
x:-0.61803398874989490 y:-0.61803398874989468
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1
f(some_starting_x) generally converges to render an x, such that 1 + 1 / x is x again and so meeting the stopping condition.
Better routines can prove that if x is reasonably close, the while loop will eventually get close to the desired answer, yet even then, an oscillation, as shown above is possible. Thus using an iteration limit or close enough test is needed. Usually the 2 oscillation values, when close, they are massaged (e.g. averaged) to form the best answer. If not close, the looping simply failed to find a stable answer.
Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?
Inadequate testing.
Morale of the story:
Do not rely on only floating point equality, except in select cases.
f() was not a select case and deserved additional stopping code.
Ref: Two x with math property: x = 1 + 1/x:
x1 = 1.6180339887498948482045868343656...
x2 = -0.61803398874989484820458683436564...
Note x1*x2 == -1. x1 is the Golden_ratio φ.

Can we assume that x == (int)sqrt(x * x) for all positive integers?

In C++ the sqrt function operates only with double values.
If we use integers (unsigned long long) can we be sure that
x == sqrt(x * x)
for any positive x where x * x <= MAXIMUM_VALUE?
Is it depend on the machine architecture and compiler?
In Java, Math.sqrt(x) takes a double value. You stated that x is such that x * x is below Integer.MAX_VALUE. Every integer is perfectly representable in double - double in java is explicitly defined as an iEEE-754 style double with a 52-bit mantissa; therefore in java a double can perfectly represent all integral values between -2^52 and +2^52, which easily covers all int values (as that is defined as signed 32-bit on java), but it does not cover all long values. (Defined as signed 64-bit; 64 is more than 52, so no go).
Thus, x * x loses no precision when it ends up getting converted from int to double. Then, Math.sqrt() on this number will give a result that is also perfectly representable as a double (because it is x, and given that x*x fits in an int, x must also fit), and thus, yes, this will always work out for all x.
But, hey, why not give it a shot, right?
public static void main(String[] args) {
int i = 1;
while (true) {
if (i * i < 0) break;
int j = (int) Math.sqrt(i * i);
if (i != j) System.out.println("Oh dear! " + i + " -> " + j);
i++;
}
System.out.println("Done at " + i);
}
> Done at 46341
Thus proving it by exhaustively trying it all.
Turns out, none exist - any long value such that x * x still fits (thus, is <2^63-1) has the property that x == (long) Math.sqrt(x * x);. This is presumably because at x*x, the number fits perfectly in a long, even if not all integer numbers that are this large do. Proof:
long p = 2000000000L;
for (; true; p++) {
long pp = p * p;
if (pp < 0) break;
long q = (long) Math.sqrt(pp);
if (q != p) System.out.println("PROBLEM: " + p + " -> " + q);
}
System.out.println("Abort: " + p);
> Abort: 3037000500
Surely if any number exists that doesn't hold, there is at least one in this high end range. Starting from 0 takes very long.
But do we know that sqrt will always return an exact value for a perfect square, or might it be slightly inaccurate?
We should - it's java. Unlike C, almost everything is 'well defined', and a JVM cannot legally call itself one if it fails to produce the exact answer as specified. The leeway that the Math.sqrt docs provide is not sufficient for any answer other than precisely x to be a legal implementation, therefore, yes, this is a guarantee.
In theory the JVM has some very minor leeway with floating point numbers, which strictfp disables, but [A] that's more about using 80-bit registers to represent numbers instead of 64, which cannot possibly ruin this hypothesis, and [B] a while back a java tag question showed up to show strictfp having any effect on any hardware and any VM version and the only viable result was a non-reproducible thing from 15 years ago. I feel quite confident to state that this will always hold, regardless of hardware or VM version.
I think we can believe.
Type casting a floating point number to an integer is to take only integer part of it. I believe you may concern, for example, sqrt(4) yields a floating point number like 1.999...9 and it is type casted to 1. (Yielding 2.000...1 is fine because it will be type casted to 2.)
But the floating number 4 is like
(1 * 2-0 + 0 + 2-1 + ... + 0 * 2-23) * 22
according to Floating-point arithmetic.
Which means, it must not be smaller than 4 like 3.999...9. So also, sqrt of the number must not be smaller than
(1 * 2-0) * 2
So sqrt of a square of an integer will at least yield a floating point number greater than but close enough to the integer.
Just try it. Yes it works in Java, for non-negative numbers. Even works for long contrary to common opinion.
class Code {
public static void main(String[] args) throws Throwable {
for (long x=(long)Math.sqrt(Long.MAX_VALUE);; --x) {
if (!(x == (long)Math.sqrt(x * x))) {
System.err.println("Not true for: "+x);
break;
}
}
System.err.println("Done");
}
}
(The first number that doesn't work is 3037000500L which goes negative when squared.)
Even for longs, the range of testable values is around 2^31 or 2*10^9 so for something this trivial it is reasonable to check every single value. You can even brute force reasonable cryptographic functions for 32-bit values - something more people should realise. Won't work so well for the full 64 bits.
BigInteger - sqrt(since 9)
Use cases requiring tighter constraint over possibilities of overflow can use BigInteger
BigInteger should work for any practical use case.
Still for normal use case, this might not be efficient.
Constraints
BigInteger Limits
BigInteger must support values in the range -2^Integer.MAX_VALUE (exclusive) to +2^Integer.MAX_VALUE (exclusive) and may support values outside of that range. An ArithmeticException is thrown when a BigInteger constructor or method would generate a value outside of the supported range. The range of probable prime values is limited and may be less than the full supported positive range of BigInteger. The range must be at least 1 to 2500000000.
Implementation Note:
In the reference implementation, BigInteger constructors and operations throw ArithmeticException when the result is out of the supported range of -2^Integer.MAX_VALUE (exclusive) to +2^Integer.MAX_VALUE (exclusive).
Array size limit when initialized as byte array
String length limit when initialized as String
Definitely may not support 1/0
jshell> new BigInteger("1").divide(new BigInteger("0"))
| Exception java.lang.ArithmeticException: BigInteger divide by zero
| at MutableBigInteger.divideKnuth (MutableBigInteger.java:1178)
| at BigInteger.divideKnuth (BigInteger.java:2300)
| at BigInteger.divide (BigInteger.java:2281)
| at (#1:1)
An example code
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
public class SquareAndSqrt {
static void valid() {
List<String> values = Arrays.asList("1", "9223372036854775807",
"92233720368547758079223372036854775807",
new BigInteger("2").pow(Short.MAX_VALUE - 1).toString());
for (String input : values) {
final BigInteger value = new BigInteger(input);
final BigInteger square = value.multiply(value);
final BigInteger sqrt = square.sqrt();
System.out.println("value: " + input + System.lineSeparator()
+ ", square: " + square + System.lineSeparator()
+ ", sqrt: " + sqrt + System.lineSeparator()
+ ", " + value.equals(sqrt));
System.out.println(System.lineSeparator().repeat(2)); // pre java 11 - System.out.println(new String(new char[2]).replace("\0", System.lineSeparator()));
}
}
static void mayBeInValid() {
try {
new BigInteger("2").pow(Integer.MAX_VALUE);
} catch (ArithmeticException e) {
System.out.print("value: 2^Integer.MAX_VALUE, Exception: " + e);
System.out.println(System.lineSeparator().repeat(2));
}
}
public static void main(String[] args) {
valid();
mayBeInValid();
}
}
in cmath library sqrt function always convert argument to double or float so the range of double or float much more than unsigned long long so it always give positive.
for reference you can use
https://learn.microsoft.com/en-us/cpp/standard-library/cmath?view=msvc-170,
https://learn.microsoft.com/en-us/cpp/c-language/type-float?view=msvc-170

verifying value equals exactly 0.00

I'm looking to calculate if a value of a variable in java is equal to 0.00 which is currently defined as a big decimal .
I have tried a variety of ways to do this including:
tempListPrice.getAmount() == 0.00;
tempListPrice.getAmount().equals(0.00);
public static final zeroed = 0.00
tempListPrice.getAmount().equals(zeroed);
Keep in mind, I've done quite a bit of googling to try to find an easy way to compare this. How do I compare the value of tempListPrice to see if it equals 0.00 , which is defined earlier as a big decimal datatype.
How do I do this? (Sorry Im quite new to Java).
Thanks
Okay, upon further research you will want equals or compareTo. You probably want
tempListPrice.getAmount().compareTo(BigDecimal.ZERO);
or
tempListPrice.getAmount().equals(BigDecimal.ZERO);
But honestly need to decide which better fits the semantics of your program. Keep in mind that the reason this is tricky is because floats don't really have a notion of exactly equals, they're intrinsically fuzzy, approximate entities. You will need to carefully review your code to make sure this is valid for your circumstances.
Anyway, the following points from the Javadoc should be enough for you to decide:
(equals) Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
cf.
(compareTo) Compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=). The suggested idiom for performing these comparisons is: (x.compareTo(y) 0), where is one of the six comparison operators.
BigDecimal val = new BigDecimal("0.00");
BigDecimal test = new BigDecimal("0.00");
boolean isEqual = val.equals(test);
System.out.println("val " + val.toString() + " test " + test + " comp = " + isEqual);
outputs
val 0.00 test 0.00 comp = true
BigDecimal val = new BigDecimal("0.000");
BigDecimal test = new BigDecimal("0.00");
boolean isEqual = val.equals(test);
System.out.println("val " + val.toString() + " test " + test + " comp = " + isEqual);
outputs
val 0.000 test 0.00 comp = false
As the prior answers have noted, you need to be sure that the methods described meet your requirements. If, as the variable name implies, you are comparing currency amounts to zero, an alternative might be to store the amount as two int variables, as follows (assumes dollars but could be any other currency):
public boolean isZero (int dollars, int cents) {
return (dollars == 0 && cents == 0);
}
In short, is a BigDecimal variable necessary for your purposes?

how to validate float against specific format in java?

I want to validate the float type data. I get the rules defined as
int precision1 = 3;
int precision2 = 2;
The above rule indicates the data format should be of 999.99
The expected validation of 1234.56, should fail from the above rule.
Also expected validation should fail for float 123.456 from the above rule.
Expected validation of 123.33 should be successful.
Any thoughts would be appreciated on validation process.
Thanks,
Kaygee
Quick and dirty:
int precision1 = 3;
int precision2 = 2;
float var = 123.12f;
String varStr = String.valueOf(var);
if (varStr.length() == (precision1 + precision2 + 1) && varStr.indexOf(".") == precision1) {
System.out.println(true);
} else {
System.out.println(false);
}
The user inputs a String, it is converted somewhere to float, and you try to validate the float. You can't do it like that.
The String representation of a float is a human artifact. Plus, a float tends to only approximate (very closely) the value of a converted String.
If you want to validate the precision the only way to go is to validate the user input String. And store it like a String if you want to keep the precision.
If you need a number, for instance to compute with, use a BigDecimal. It is designed to keep the precision exactly as given (important for financial calculations). They are not very nice to work with, but that's the price you pay if you want exact non-integer values.
EDIT
How to use a BigDecimal.
BigDecimal b1 = new BigDecimal("1234.23");
System.out.println(b1 + " scale was " + b1.scale()); 2
BigDecimal b2 = new BigDecimal("1234.523536223");
System.out.println(b2 + " scale was " + b2.scale()); // 9
You can obviously test the "precision" by using the scale. If b1.scale() == 2 || b1.scale() == 3 => valid.

How to add two java.lang.Numbers?

I have two Numbers. Eg:
Number a = 2;
Number b = 3;
//Following is an error:
Number c = a + b;
Why arithmetic operations are not supported on Numbers? Anyway how would I add these two numbers in java? (Of course I'm getting them from somewhere and I don't know if they are Integer or float etc).
You say you don't know if your numbers are integer or float... when you use the Number class, the compiler also doesn't know if your numbers are integers, floats or some other thing. As a result, the basic math operators like + and - don't work; the computer wouldn't know how to handle the values.
START EDIT
Based on the discussion, I thought an example might help. Computers store floating point numbers as two parts, a coefficient and an exponent. So, in a theoretical system, 001110 might be broken up as 0011 10, or 32 = 9. But positive integers store numbers as binary, so 001110 could also mean 2 + 4 + 8 = 14. When you use the class Number, you're telling the computer you don't know if the number is a float or an int or what, so it knows it has 001110 but it doesn't know if that means 9 or 14 or some other value.
END EDIT
What you can do is make a little assumption and convert to one of the types to do the math. So you could have
Number c = a.intValue() + b.intValue();
which you might as well turn into
Integer c = a.intValue() + b.intValue();
if you're willing to suffer some rounding error, or
Float c = a.floatValue() + b.floatValue();
if you suspect that you're not dealing with integers and are okay with possible minor precision issues. Or, if you'd rather take a small performance blow instead of that error,
BigDecimal c = new BigDecimal(a.floatValue()).add(new BigDecimal(b.floatValue()));
It would also work to make a method to handle the adding for you. Now I do not know the performance impact this will cause but I assume it will be less than using BigDecimal.
public static Number addNumbers(Number a, Number b) {
if(a instanceof Double || b instanceof Double) {
return a.doubleValue() + b.doubleValue();
} else if(a instanceof Float || b instanceof Float) {
return a.floatValue() + b.floatValue();
} else if(a instanceof Long || b instanceof Long) {
return a.longValue() + b.longValue();
} else {
return a.intValue() + b.intValue();
}
}
The only way to correctly add any two types of java.lang.Number is:
Number a = 2f; // Foat
Number b = 3d; // Double
Number c = new BigDecimal( a.toString() ).add( new BigDecimal( b.toString() ) );
This works even for two arguments with a different number-type. It will (should?) not produce any sideeffects like overflows or loosing precision, as far as the toString() of the number-type does not reduce precision.
java.lang.Number is just the superclass of all wrapper classes of primitive types (see java doc). Use the appropriate primitive type (double, int, etc.) for your purpose, or the respective wrapper class (Double, Integer, etc.).
Consider this:
Number a = 1.5; // Actually Java creates a double and boxes it into a Double object
Number b = 1; // Same here for int -> Integer boxed
// What should the result be? If Number would do implicit casts,
// it would behave different from what Java usually does.
Number c = a + b;
// Now that works, and you know at first glance what that code does.
// Nice explicit casts like you usually use in Java.
// The result is of course again a double that is boxed into a Double object
Number d = a.doubleValue() + (double)b.intValue();
Use the following:
Number c = a.intValue() + b.intValue(); // Number is an object and not a primitive data type.
Or:
int a = 2;
int b = 3;
int c = 2 + 3;
I think there are 2 sides to your question.
Why is operator+ not supported on Number?
Because the Java language spec. does not specify this, and there is no operator overloading. There is also not a compile-time natural way to cast the Number to some fundamental type, and there is no natural add to define for some type of operations.
Why are basic arithmic operations not supported on Number?
(Copied from my comment:)
Not all subclasses can implement this in a way you would expect. Especially with the Atomic types it's hard to define a usefull contract for e.g. add.
Also, a method add would be trouble if you try to add a Long to a Short.
If you know the Type of one number but not the other it is possible to do something like
public Double add(Double value, Number increment) {
return value + Double.parseDouble(increment.toString());
}
But it can be messy, so be aware of potential loss of accuracy and NumberFormatExceptions
Number is an abstract class which you cannot make an instance of. Provided you have a correct instance of it, you can get number.longValue() or number.intValue() and add them.
First of all, you should be aware that Number is an abstract class. What happens here is that when you create your 2 and 3, they are interpreted as primitives and a subtype is created (I think an Integer) in that case. Because an Integer is a subtype of Number, you can assign the newly created Integer into a Number reference.
However, a number is just an abstraction. It could be integer, it could be floating point, etc., so the semantics of math operations would be ambiguous.
Number does not provide the classic map operations for two reasons:
First, member methods in Java cannot be operators. It's not C++. At best, they could provide an add()
Second, figuring out what type of operation to do when you have two inputs (e.g., a division of a float by an int) is quite tricky.
So instead, it is your responsibility to make the conversion back to the specific primitive type you are interested in it and apply the mathematical operators.
The best answer would be to make util with double dispatch drilling down to most known types (take a look at Smalltalk addtition implementation)

Categories