Compute using different variable types - java

I'm reading Java: The Complete Reference by Herbert Schildt right now and here is one thing that is not really clear for me. In the chapter about Integers it says that the long type should be used when big, whole numbers are needed. The code example from the book:
// Compute distance light travels using long variables
class Light {
public static void main(String args[]) {
int lightspeed;
long days;
long seconds;
long distance;
// approximate speed of light in miles per second
lightspeed = 18600;
days = 1000; // specify number of days here
seconds = days * 24 * 60 * 60; // convert to seconds
distance = lightspeed * seconds; // compute distance
System.out.print("In " + days + " days light will travel about " + distance + " miles.");
}
}
Upon execution console provides me with such the result:
"In 1000 days light will travel about 16070400000000 miles."
According by my logic, days and seconds vars should be int too, while their values are less than 2 147 483 647.
At this example days = 1000 and seconds = 86 400 000, so the values aren't that big.
But when I declare variables like this:
int lightspeed, days, seconds;
long distance;
I've strange result and for now I can accept it at no way.
"In 1000 days light will travel about -1367621632 miles."
Help me please.

When both seconds and lightspeed are declared as int, lightspeed * seconds is computed as the multiplication of ints, which causes an overflow (since 16070400000000 > 2147483647).
You can still leave them as int if you cast lightspeed or seconds to long, which will force the multiplication to be performed on longs :
distance = (long)lightspeed * seconds;
or
distance = lightspeed * (long)seconds;

Yes, only the distance is relevant, as Eran pointed out.
This behaviour is quite easy to remember if you just think of divison.
as above a / b performs integer division (truncation) given that a and b are integers whereas
(double) a / b
would return a double.
Note that the cast always refers to what follows after it. so in the case of an expression comprising of two integers only the first integer is casted to double.
`

Related

How do i convert a double into an int when converting minutes to seconds in java

Trying to convert minutes to seconds with the starting data being a double,
so for example 33.51 seconds (33 mins 51 seconds)
How to convert that to an int when converting to seconeds only
My code for now would accept it if there was no double point to begin with, so i only used ints only
and it seems to work, but when i have that double in there it doesnt, is therre a better approach to it .
Heres my code for now
public class Runner {
//fields setting up the variables
String MembershipID;
String name;
int time ;
//constructor1 filling in the details
public Runner(String Mem, String na, int ti) {
MembershipID = Mem;
name = na; //This
time = ti;
}
public String getMembershipID() {
return MembershipID;
}
public String getName() {
return name;
} //setting it up for the main method from the constructor fields above
public int getTime() {
int mins = time *60;
return mins;
}
public static void main(String[] args) { //initializing the membership name and time,
Runner a = new Runner("RF23", "George Formsby", 33); //Creating a new instance of
Runner, filling out the details from the sample data provided.
Runner b = new Runner("RG89", "Neil Innes", 32);
Runner c = new Runner("ST200", "Sandy Denny", 30); // With mem and na being
represented from my constructor
System.out.println("MembershipID is: " + a.getMembershipID());
System.out.println("Name is: " + a.getName());
System.out.println("Time in seconds: " + a.getTime());
System.out.println("MembershipID is: " + b.getMembershipID());
System.out.println("Name is: " + b.getName());
System.out.println("Time is: " + b.getTime());
System.out.println("MembershipID is: " + c.getMembershipID());
System.out.println("Name is: " + c.getName());
System.out.println("Time is: " + c.getTime());
}
}
Seems like simple enough math.
Convert minutes + seconds to just seconds
int minutes = ...;
int seconds = ...;
int totalSeconds = minutes * 60 + seconds;
Convert minutes (as a double) to seconds
double time = 12.33;
int seconds = (int) (0.5 + time * 60);
Explanation: when rounding to an int, java lops off the decimal parts. We want to round to the nearest second, which can be trivially accomplished (for positive numbers!) by adding 0.5 to it and then casting to int.
NB: See note below!
Convert seconds (as int) to minutes (as double)
int totalSeconds = ....;
double time = time / 60.0;
Explanation: In java, The syntactic construct x / y is considered to be 'integer division' if x and y are both integral data types (byte, short, int, long, or char). It is considered 'floating point division' if either x or y is a floating point type (float or double). integer division will lop off the decimal digits (so, it rounds down for positive results, and rounds up for negative results). Thus, 90 / 2, for example, resolves to 1. Not 1.5. On the other hand, 90 / 2.0 resolves to 1.5, because at least one of the two numbers is a double (2.0 is a double constant, 2 is an int constant). Hence why we divide by 60.0 and not 60.
NB: Important thing to think about: PRECISION.
Computers aren't magical, and double is precisely defined as consisting of exactly 64 bits.
You can't store one of an infinite sequence of options in a finite storage space, so, computers cannot store numbers perfectly. A 64-bit storage space has the ability to give you at most 2^64 different 'options'. If the storage space is storing a number, that means there are at most 2^64 numbers it could possibly store there, and all other numbers therefore simply cannot be represented by it. Somebody needs to go out and define which numbers are 'blessed' - capable of being stored. Then someone needs to define what happens if you attempt to store a non-blessed number in them.
For the integer data types, this is easy: int (32-bit) can store 2^32 numbers. Which numbers are blessed? Simply -2^31 to +2^31 -1. When you attempt to store something above or below it, the numbers just loop around:
int reallyLarge = Integer.MAX_VALUE; // this is 2^31-1.
int surelyThisIsEvenLarger = reallyLarge + 1;
Actually, surelyThisIsEvenLarger is negative number instead. It looped around.
For double and float it is way more complicated. Even between just 0 and 1 there are infinite numbers! The blessed numbers are chosen by more or less throwing darts at the numberline, focusing about half the darts close to 1.0, with fewer and fewer darts hitting the number line as you move away from 1.0. Eventually, at around 2^52, the 'distance' between any 2 darts is higher than 1.0, even.
It's a bit like how we humans do it: We cannot 'represent' 1 divided by 3, at all. 0.333333.... it never ends.
To make matters worse, computers count in binary and not decimal. So, where we humans can e.g. do '1 divided by 10' (that's 0.1, so it is a blessed number in the system of 'a human writes it down in decimal on a bit of paper that has room for about 10 digits'), computers cannot do that, either.
Thus, most of the 'take these number of seconds and turn them into a double' values, are not actually blessed, so it is important to realize what happens when you try to make a double that isn't blessed: The computer will round it off to the closest blessed number. You can't ask for the error (the amount it rounded by), or ask it not to do this; not with double, anyway.
If you do enough back-and-forth math on them, those errors compound and eventually will be flat out wrong. That's one of the many reasons why you should most definitely never, ever use double to store money values. For race monitoring you're running into a similar situation here, best not to use them. Better to pick an atomic unit and store in those. For example, why not store in millis? The current record for fastest mile is 3:43.13. In 'millis', that becomes long fastestMile = 223130; - no need to involve those nasty doubles with their bizarro rounding behaviours.

java.time.Duration.dividedBy(long) vs java.time.Duration.dividedBy(Duration)

I am trying to divide two time durations (java.time.Duration) and to perform that there are two methods
java.time.Duration#dividedBy(long) which returns an object of type Duration itself but due to some API restrictions I can not use this version.
java.time.Duraiton#dividedBy(Duration) which returns a long and returns number of times one duration occurs within other duration but here we lose the precision as it does not care about the remainder.
Is there any way out to perform this division and get the result with remainder.
Duration.ofHours(1).dividedBy(7L); //returns 8M34.28571 seconds
Duration.ofHours(1).dividedBy(Duration.ofSeconds(7L)) // returns 514 seconds
I have a restriction not to use the first way. Can I get the results using some other way?
EDIT: (from comments) I am not allowed to convert a Duration instance to an ordinal value, e.g. milliseconds.
Convert to milliseconds/nanoseconds, then divide their ordinal value(s), then create a new Duration object from the result.
When the easy ways are forbidden, you can of course do it the more cumbersome way. The following is similar to how we learned to do division by hand in school.
Duration dividend = Duration.ofHours(1);
long divisor = 7;
long minutes = dividend.toMinutes() / divisor;
dividend = dividend.minusMinutes(minutes * divisor);
long seconds = dividend.toSeconds() / divisor;
dividend = dividend.minusSeconds(seconds * divisor);
long nanoseconds = dividend.toNanos() / divisor;
Duration result = Duration.ofMinutes(minutes)
.plusSeconds(seconds)
.plusNanos(nanoseconds);
System.out.println(result);
Result output:
PT8M34.285714285S
Your requirements are not that precise, so I can’t be sure I haven’t used a forbidden method, though.

Why does adding a sum to a long value lead to a subtraction?

I encountered a troublesome issue and I can't really explain to myself why it is appearing.
Basically I want to add time to a timestamp (a simple long).
I understand it the following. If I add time to a timestamp I end in the future. If I subtract time to the timestamp I end in the past.
In my example it is the other way around. If I add something to my timestamp it is reduced and if I subtract something is added.
public class MyClass {
public static void main(String args[]) {
static final int MONTH_IN_SECONDS = 2629743;
final long current = System.currentTimeMillis();
System.out.println("Current: " + current);
final long future = System.currentTimeMillis() + (MONTH_IN_SECONDS * 1000 * 3);
System.out.println("Addition: " + future);
final long past = System.currentTimeMillis() - (MONTH_IN_SECONDS * 1000 * 3);
System.out.println("Subtraction: " + past);
}
}
Result (compare the first 5 chars):
Current: 1582275101365
Addition: 1581574395774 // smaller than current even though it should be greater
Subtraction: 1582975806958 // great than current even though it should be smaller
Why does this happend? Does the term (MONTH_IN_SECONDS * 1000 * 3) overflow because it is only an Integer and thus the calculation does not work (or ends in a negative value)?
If I change the term to (MONTH_IN_SECONDS * 1000L * 3) it seems to work correctly. Is it because the complete term is casted to a long?
The problem is here:
(MONTH_IN_SECONDS * 1000 * 3)
That's integer multiplication that's overflowing, and resulting in a negative number:
System.out.println((MONTH_IN_SECONDS * 1000 * 3));
That outputs -700705592. You'd have to declare MONTH_IN_SECONDS as long, or otherwise change the expression so that the result is long-typed.
Does the term (MONTH_IN_SECONDS * 1000 * 3) overflow because it is
only an Integer and thus the calculation does not work (or ends in a
negative value)?
Month in seconds? Google says 2,630,000. (Though I see you have 2629743.)
2,630,000 * 1000 * 3 = 7,890,000,000
Integer.MAX_VALUE = 2^31 = 2,147,483,648
So yeah, it's an integer overflow.

Loss of precision - Java

Problem Statement:
Write a method whatTime, which takes an int, seconds, representing the number of seconds since midnight on some day, and returns a String formatted as "::". Here, represents the number of complete hours since midnight, represents the number of complete minutes since the last complete hour ended, and represents the number of seconds since the last complete minute ended. Each of , , and should be an integer, with no extra leading 0's. Thus, if seconds is 0, you should return "0:0:0", while if seconds is 3661, you should return "1:1:1"
My Algorithm:
Here is how my algorithm is supposed to work for the input 3661:
3661/3600 = 1.016944 -> This means the number of hours is 1
Subtract the total number of hours elapsed i.e. 1.016944-1=0.016944
Multiply this with 60 i.e. 0.016944*60=1.016666 -> The number of minutes elapsed is equal to 1
Subtract the total number of minutes elapsed i.e. 1.01666-1=0.01666. Multiply this with 60. This would yield the number of seconds elapsed.
The output produced however is 1:1:0. I tried to use a print statement and it appears that the value of 'answer3' variable is 0.999 and that is why prints the integer part (0). I tried to use the Math.ceil() function to round up the value and it produces a correct output. However I can only score about 60/250 points when I submit my code (TopCoder SRM 144 Div2) . Any insight for improving the algorithm will be helpful.
public class Time
{
public String whatTime(int seconds)
{
double answer1,answer2,answer3; int H;
answer1=(double)seconds/3600;
H=(int)answer1;
answer2=(double)((answer1-H)*60);
int M=(int)answer2;
answer3=answer2-M;
answer3=(double)answer3*60;
int S=(int)answer3;
String answer=Integer.toString(H);
answer=Integer.toString(H)+":"+Integer.toString(M)+":"+Integer.toString(S);
return answer;
}
}
public String whatTime(int seconds) {
int secondVal = seconds % 60;
int minutes = seconds / 60;
int minuteVal = minutes % 60;
int hours = minutes / 60;
int hourVal = hours % 24;
int daysVal = hours / 24;
String answer = "" + daysVal + ":" + hourVal + ":" + minuteVal + ":" + secondVal;
return answer;
}
Could do the formatting more elegantly, but that's the basic idea.
Avoid floating point values, and work entirely with ints or longs.
You could solve this by working with ints :
3661/3600 = 1.016944 -> This means the number of hours is 1
Subtract the number of hours * 3600 - i.e. 3661-(1*3600) = 61
61/60 = 1.0166666 -> The number of minutes elapsed is equal to 1
Subtract the number of minutes * 60 i.e. 61-(1*60)=1. This yields the number of seconds elapsed.

Get a less specific number - from another number

Right, sorry I can't think of the correct words to google this. So I'll have to ask.
I've got a long (System.currentTimeMillis())
Lets say
3453646345345345
I want to remove the last six (or other number of) digits and I think I can do this doing some kind of bit shift?
so I would end up with
3453646345
EDIT
I wanted to get the System.currentTimeMillis() within a time box, so if I ask for the time then ask again 29 seconds later it will return the same number but if I ask 31 seconds later it will return a different number. With the 30 second timebox being configurable.
You have to simply divide it by 1M long shorter = System.currentTimeMillis() / 1000000L;
To build on #Yob's answer, you can make the number of digits to remove configurable by creating a method like this:
public long removeDigits(long number, int digitsToRemove) {
return number / (long)Math.pow(10, digitsToRemove);
}
Depending on what you want to do (in base 10, I assume), you can do this:
int64_t radix = 1000000; // or some other power of 10
x -= x%radix; // last 6 decimal digits are now 0
// e.g: from 3453646345345345 to 3453646345000000
Or this (as in the previous answer):
x /= radix; // last 6 decimal digits are gone, the result rounded down
// e.g: from 3453646345345345 to 3453646345
Response to Edit
For your purposes, you could change radix in the modulus example to 30000:
int64_t timeInterval = 30000;
displayTime = actualTime - (actualTime % timeInterval);
Where displayTime and actualTime are in milliseonds. displayTime will, in this case, have a (rounded-down) granularity of 30 seconds while remaining a unit of milliseconds.
To have a rounded up granularity, you can do the following:
int64_t timeInterval = 30000;
int64_t modulus = actualTime % timeInterval;
displayTime = actualTime - modulus + (modulus?timeInterval:0);
Though, based on what you are asking, it seems you just want to update a display value only every few ticks. The following will work as well:
if((actualTime - displayTime) >= timeInterval){
displayTime = actualTime - (actualTime % timeInterval);
}
Pardon the C integer types, I just prefer to be unambiguous about the width of integer I'm using :P.

Categories

Resources