Different answer when converting a Double to an Int - Java vs .Net - java

In C# if I want to convert a double (1.71472) to an int then I get the answer 2. If I do this in Java using intValue() method, I get 1 as the answer.
Does Java round down on conversions?
Why do the Java API docs have such scant information about their classes i.e.
Returns the value of the specified
number as an int. This may involve
rounding or truncation.
A bit more info about the rounding would have been helpful!

Java rounds toward zero when narrowing from a floating point to an integer type—and so does C#, when you use the casting conversion. It's Convert.ToInt32 that rounds:
double d = 1.71472;
int x = (int) d; // x = 1
int y = Convert.ToInt32(d); // y = 2
Details can be found in the Java Language Specification. Note that while the documentation cited in Number leaves options open for subclasses, the documentation on the concrete boxing types, like Double, is explicit about the implementation:
Returns the value of this Double as an
int (by casting to type int).
When using BigDecimal, you can specify one of eight different rounding policies.

Java rounds toward zero when narrowing from a floating point to an integer type.
There's more documentation about the general rules in the Java Language Specification. Note that while the documentation in Number leaves options open for subclasses, the documentation on the concrete boxing types is more explicit:
Returns the value of this Double as an
int (by casting to type int).
When using BigDecimal, you can specify one of eight different rounding policies.

Jon Skeet is correct, but something else to watch for is that .NET uses Banker's Rounding as its rounding algorithm. When you're halfway between round towards the even integer.

Related

Exact conversion from float to int

I want to convert float value to int value or throw an exception if this conversion is not exact.
I've found the following suggestion: use Math.round to convert and then use == to check whether those values are equal. If they're equal, then conversion is exact, otherwise it is not.
But I've found an example which does not work. Here's code demonstrating this example:
String s = "2147483648";
float f = Float.parseFloat(s);
System.out.printf("f=%f\n", f);
int i = Math.round(f);
System.out.printf("i=%d\n", i);
System.out.printf("(f == i)=%s\n", (f == i));
It outputs:
f=2147483648.000000
i=2147483647
(f == i)=true
I understand that 2147483648 does not fit into integer range, but I'm surprised that == returns true for those values. Is there better way to compare float and int? I guess it's possible to convert both values to strings, but that would be extremely slow for such a primitive function.
floats are rather inexact concepts. They are also mostly pointless unless you're running on at this point rather old hardware, or interacting specifically with systems and/or protocols that work in floats or have 'use a float' hardcoded in their spec. Which may be true, but if it isn't, stop using floats and start using double - unless you have a fairly large float[] there is zero memory and performance difference, floats are just less accurate.
Your algorithm cannot fail when using int vs double - all ints are perfectly representable as double.
Let's first explain your code snippet
The underlying error here is the notion of 'silent casting' and how java took some intentional liberties there.
In computer systems in general, you can only compare like with like. It's easy to put in exact terms of bits and machine code what it means to determine whether a == b is true or false if a and b are of the exact same type. It is not at all clear when a and b are different things. Same thing applies to pretty much any operator; a + b, if both are e.g. an int, is a clear and easily understood operation. But if a is a char and b is, say, a double, that's not clear at all.
Hence, in java, all binary operators that involve different types are illegal. In basis, there is no bytecode to directly compare a float and a double, for example, or to add a string to an int.
However, there is syntax sugar: When you write a == b where a and b are different types, and java determines that one of two types is 'a subset' of the other, then java will simply silently convert the 'smaller' type to the 'larger' type, so that the operation can then succeed. For example:
int x = 5;
long y = 5;
System.out.println(x == y);
This works - because java realizes that converting an int to a long value is not ever going to fail, so it doesn't bother you with explicitly specifying that you intended the code to do this. In JLS terms, this is called a widening conversion. In contrast, any attempt to convert a 'larger' type to a 'smaller' type isn't legal, you have to explicitly cast:
long x = 5;
int y = x; // does not compile
int y = (int) x; // but this does.
The point is simply this: When you write the reverse of the above (int x = 5; long y = x;), the code is identical, it's just that compiler silently injects the (long) cast for you, on the basis that no loss will occur. The same thing happens here:
int x = 5;
long y = 10;
long z = x + y;
That compiles because javac adds some syntax sugar for you, specifically, that is compiled as if it says: long z = ((long) x) + y;. The 'type' of the expression x + y there is long.
Here's the key trick: Java considers converting an int to a float, as well as an int or long to a double - a widening conversion.
As in, javac will just assume it can do that safely without any loss and therefore will not enforce that the programmer explicitly acknowledges by manually adding the cast. However, int->float, as well as long->double are not actually entirely safe.
floats can represent every integral value between -2^23 to +2^23, and doubles can represent every integral value between -2^52 to +2^52 (source). But int can represent every integral value between -2^31 to +2^31-1, and longs -2^63 to +2^63-1. That means at the edges (very large negative/positive numbers), integral values exist that are representable in ints but not in floats, or longs but not in doubles (all ints are representable in double, fortunately; int -> double conversion is entirely safe). But java doesn't 'acknowledge' this, which means silent widening conversions can nevertheless toss out data (introduce rounding) silently.
That is what happens here: (f == i) is syntax sugared into (f == ((float) i)) and the conversion from int to float introduces the rounding.
The solution
Mostly, when using doubles and floats and nevertheless wishing for exact numbers, you've already messed up. These concepts fundamentally just aren't exact and this exactness cannot be sideloaded in by attempting to account for error bands, as the errors introduced due to the rounding behaviour of float and double cannot be tracked (not easily, at any rate). You should not be using float/double as a consequence. Either find an atomary unit and represent those in terms of int/long, or use BigDecimal. (example: To write bookkeeping software, do not store finance amounts as a double. do store them as 'cents' (or satoshis or yen or pennies or whatever the atomic unit is in that currency) in long, or, use BigDecimal if you really know what you are doing).
I want an answer anyway
If you're absolutely positive that using float (or even double) here is acceptable and you still want exactness, we have a few solutions.
Option 1 is to employ the power of BigDecimal:
new BigDecimal(someDouble).intValueExact()
This works, is 100% reliable (unless float to double conversion can knock a non-exact value into an exact one somehow, I don't think that can happen), and throws. It's also very slow.
An alternative is to employ our knowledge of how the IEEE floating point standard works.
A real simple answer is simply to run your algorithm as you wrote it, but to add an additional check: If the value your int gets is below -2^23 or above +2^23 then it probably isn't correct. However, there are still a smattering of numbers below -2^23 and +2^23 that are perfectly representable in both float and int, just, no longer every number at that point. If you want an algorithm that will accept those exact numbers as well, then it gets much more complicated. My advice is not to delve into that cesspool: If you have a process where you end up with a float that is anywhere near such extremes, and you want to turn them to int but only if that is possible without loss, you've arrived at a crazy question and you need to rewire the parts that you got you there instead!
If you really need that, instead of trying to numbercrunch the float, I suggest using the BigDecimal().intValueExact() trick if you truly must have this.

Base 2 custom method and approximation in Java

I'm writing a method of a Network Calculator that calculates how many bits are needed giving a number of hosts. This is the piece of code that is giving me some problems:
int hostBits = (int) Math.ceil(Math.log(hosts) / Math.log(2));
It works fine with small numbers, but the problem comes up when I enter big ones, like 2^31 - 2, because the result of Math.log(hosts) / Math.log(2) (changing the base of the logarithm), that should be 31.0, actually is 31.000000000000004.
Then, Math.ceil(...) brings 31.000000000000004 to 32 and not to 31 as I want.
Does anyone have an idea how to solve this?
Not the problem.
The problem is only that when a number is divided , for example two different integers (int's) the result particularly with "Math" and log methods is "precision" that it expects may well be required to be some custom level or default level of a primitive type such as double - hence the extra decimal places after the decimal point.
Two points, first, when a number is divided that technically would simply be an integer e.g. 12 / 3 = 6 with math and as a primitive type double it will always have decimal point followed by around six zero's or so and a number other than 0 at the end. Second, it could appear worse from the Math library you could get logarithmic notations from a division not decimal places.
A final point, the Math library does not know what the answer will be until it does it so using double type is an automatic conversion no matter how you write it for safety of precision and data type.
Automatic swapping of data types is known as promotion (raising precision int to a float) or demotion (lowering precision double to a float)
And to convert it to an integer it will round up or down by rules of the first following decimal place from the point, so you basically only need to put the answer into a "new Double(primativedoubletype)" object and use its "intValue" method.
NB putting an (int) cast in front of it only ensures that it is an int when assigned (generally from an expression or method) to an int variable, however it is not a way of extracting the value of another type to an int, the primitive type must be converted through its standard java type class object with its primatves converter method.
int hostBits2 = new Double((double)Math.ceil(Math.log(hosts) / Math.log(2)+1e-10)).intValue();
Try using epsilon while rounding
int hostBits2 = (int) Math.ceil(Math.log(hosts) / Math.log(2)+1e-10);

Int can be incremented by a double value

This code seems to work in Java, violating everything I thought I knew about the language:
int x = 0;
x += 7.4;
x now has the value 7. Of course, one can't just write int x = 7.4, so this behavior seems strange and inconsistent to me.
Why did the developers of Java choose such a behavior?
The question that mine was marked as a duplicate of was actually answering the "what happens" part, but not my main question: what the rationale is.
The operators for numbers do all kinds of casting which in this case converts the 7.4 double to a 7 int by rounding it.
What you have here is a Compound Assignment Operators
So what really gets executed is
x= (int)(x + 7.4)
Since x is an int and 7.4 x gets converted to double vs a Binary Numeric Promotion so you get 7.4 as an intermediate result.
The result (a double) is then cast and therefore subject to a Narrowing Primitive Conversion which rounds it to 7
Regarding the new question: Why was it done this way?
Well you can argue long if implicit conversions are a good or bad thing. Java went some kind of middle road with some conversions between primitives, their boxed types and Strings.
The += operator then has a rather simple and straight forward semantics. It really only looks strange if you consider it an increment by operator, instead of what it really is: a shorthand for a combination of operator and assignment.
Sometime back only i read about it
It will be actually
X= (int)(x + 7.4)
No. it's not inconsistent. It round-to-nearest mode.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html
A widening conversion of an int or a long value to float, or of a long value to double, may result in loss of precision - that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using IEEE 754 round-to-nearest mode (§4.2.4).
In my opinion x = 7 due to basic conversion

Java double epsilon

I'm currently in the need of an epsilon of type double (preferred are constants in java's libraries instead of own implementations/definitions)
As far as I can see Double has MIN_VALUE and MAX_VALUE as static members.
Why there is no EPSILON?
What would a epsilon<double> be?
Are there any differences to a std::numeric_limits< double >::epsilon()?
Epsilon: The difference between 1 and the smallest value greater than 1 that is representable for the data type.
I'm presuming you mean epsilon in the sense of the error in the value. I.e this.
If so then in Java it's referred to as ULP (unit in last place). You can find it by using the java.lang.Math package and the Math.ulp() method. See javadocs here.
The value isn't stored as a static member because it will be different depending on the double you are concerned with.
EDIT: By the OP's definition of epsilon now in the question, the ULP of a double of value 1.0 is 2.220446049250313E-16 expressed as a double. (I.e. the return value of Math.ulp(1.0).)
By the edit of the question, explaining what is meant by EPSILON, the question is now clear, but it might be good to point out the following:
I believe that the original question was triggered by the fact that in C there is a constant DBL_EPSILON, defined in the standard header file float.h, which captures what the question refers to. The same standard header file contains definitions of constants DBL_MIN and DBL_MAX, which clearly correspond to Double.MIN_VALUE and Double.MAX_VALUE, respectively, in Java. Therefore it would be natural to assume that Java, by analogy, should also contain a definition of something like Double.EPSILON with the same meaning as DBL_EPSILON in C. Strangely, however, it does not. Even more strangely, C# does contain a definition double.EPSILON, but it has a different meaning, namely the one that is covered in C by the constant DBL_MIN and in Java by Double.MIN_VALUE. Certainly a situation that can lead to some confusion, as it makes the term EPSILON ambiguous.
Without using Math package:
Double.longBitsToDouble(971l << 52)
That's 2^-52 (971 = 1023(double exponent bias) - 52, shift by 52 is because mantissa is stored on the first 52 bits).
It's a little quicker than Math.ulp(1.0);
Also, if you need this to compare double values, there's a really helpful article: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
double: The double data type is a double-precision 64-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. For decimal values, this data type is generally the default choice. As mentioned above, this data type should never be used for precise values, such as currency.
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
looking up at IEEE 754 you'll find the precision of epsion...
http://en.wikipedia.org/wiki/IEEE_floating_point
binary64:
Base(b)=2
precision(p)=53
machineEpsion(e) (b^-(p-1))/2=2^-53=1.11e-16
machineEpsilon(e) b^-(p-1)=2^-52=2.22e-16

Why does Math.round return a long but Math.floor return a double?

Why the inconsistency?
There is no inconsistency: the methods are simply designed to follow different specifications.
long round(double a)
Returns the closest long to the argument.
double floor(double a)
Returns the largest (closest to positive infinity) double value that is less than or equal to the argument and is equal to a mathematical integer.
Compare with double ceil(double a)
double rint(double a)
Returns the double value that is closest in value to the argument and is equal to a mathematical integer
So by design round rounds to a long and rint rounds to a double. This has always been the case since JDK 1.0.
Other methods were added in JDK 1.2 (e.g. toRadians, toDegrees); others were added in 1.5 (e.g. log10, ulp, signum, etc), and yet some more were added in 1.6 (e.g. copySign, getExponent, nextUp, etc) (look for the Since: metadata in the documentation); but round and rint have always had each other the way they are now since the beginning.
Arguably, perhaps instead of long round and double rint, it'd be more "consistent" to name them double round and long rlong, but this is argumentative. That said, if you insist on categorically calling this an "inconsistency", then the reason may be as unsatisfying as "because it's inevitable".
Here's a quote from Effective Java 2nd Edition, Item 40: Design method signatures carefully:
When in doubt, look to the Java library APIs for guidance. While there are plenty of inconsistencies -- inevitable, given the size and scope of these libraries -- there are also fair amount of consensus.
Distantly related questions
Why does int num = Integer.getInteger("123") throw NullPointerException?
Most awkward/misleading method in Java Base API ?
Most Astonishing Violation of the Principle of Least Astonishment
floor would have been chosen to match the standard c routine in math.h (rint, mentioned in another answer, is also present in that library, and returns a double, as in java).
but round was not a standard function in c at that time (it's not mentioned in C89 - c identifiers and standards; c99 does define round and it returns a double, as you would expect). it's normal for language designers to "borrow" ideas, so maybe it comes from some other language? fortran 77 doesn't have a function of that name and i am not sure what else would have been used back then as a reference. perhaps vb - that does have Round but, unfortunately for this theory, it returns a double (php too). interestingly, perl deliberately avoids defining round.
[update: hmmm. looks like smalltalk returns integers. i don't know enough about smalltalk to know if that is correct and/or general, and the method is called rounded, but it might be the source. smalltalk did influence java in some ways (although more conceptually than in details).]
if it's not smalltalk, then we're left with the hypothesis that someone simply chose poorly (given the implicit conversions possible in java it seems to me that returning a double would have been more useful, since then it can be used both while converting types and when doing floating point calculations).
in other words: functions common to java and c tend to be consistent with the c library standard at the time; the rest seem to be arbitrary, but this particular wrinkle may have come from smalltalk.
I agree, that it is odd that Math.round(double) returns long. If large double values are cast to long (which is what Math.round implicitly does), Long.MAX_VALUE is returned. An alternative is using Math.rint() in order to avoid that. However, Math.rint() has a somewhat strange rounding behavior: ties are settled by rounding to the even integer, i.e. 4.5 is rounded down to 4.0 but 5.5 is rounded up to 6.0). Another alternative is to use Math.floor(x+0.5). But be aware that 1.5 is rounded to 2 while -1.5 is rounded to -1, not -2. Yet another alternative is to use Math.round, but only if the number is in the range between Long.MIN_VALUE and Long.MAX_VALUE. Double precision floating point values outside this range are integers anyhow.
Unfortunately, why Math.round() returns long is unknown. Somebody made that decision, and he probably never gave an interview to tell us why. My guess is, that Math.round was designed to provide a better way (i.e., with rounding) for converting doubles to longs.
Like everyone else here I also don't know the answer, but thought someone might find this useful. I noticed that if you want to round a double to an int without casting, you can use the two round implementations long round(double) and int round(float) together:
double d = something;
int i = Math.round(Math.round(d));

Categories

Resources