• Frz (unregistered)

How do you get 40.9599609375 with integer Maths? It's probably some embedded system assembler magic.

• (cs)

And now for something completely different: the Y25k7 problem.

• Hinek (unregistered)

Also:

For April, June, September, November it is not checked if register3 is >= 1.

And register3 is suddenly called r3 at the end of the function, but I guess this is due to the Java translation ...

• TV John (unregistered)

'How do you get 40.9599609375 with integer Maths?'

Obviously you don't, directly. What's happening is that register 5 is being bit-shifted right 10 times and subtracted from itself. A right bit-shift is equivalent to dividing by 2, so this is (Reg 5) - (Reg 5 / 1024). Since we're talking integer maths, if Reg 5 contains 41 then it will still contain 41 afterwards, not 40.9599... etc, but you get the picture.

I used to love doing this sort of thing. 8086 could handle multiplication and division but it was so expensive in processor time that you'd go to great lengths to avoid it if possible.

• Greg (unregistered) in reply to Hinek
Hinek:
Also:

For April, June, September, November it is not checked if register3 is >= 1.

I'ld think that case is covered by the register3 >=29 check...
• (cs)

The bit mangling effectively multiplies the year by 41 then subtracts one 1024th of that result. If we revert to using floating point for a moment, and take year 1000 as an example, multiply by 41 and then subtract 1/1024 of 41000, we get 40959.9609375, and if we divide that by 1000 to recover the true multiplier, we get 40.959960937.

That's how we get such a multiplier on an integer-only machine.

None of that explains how we get to such a number, nor why we want to treat any days at all in year 0 as valid.

• Greg (unregistered) in reply to Hinek
Hinek:
Also:

For April, June, September, November it is not checked if register3 is >= 1.

I\ld think this case is covered by the register3 >= 29 check...

• (cs)

Oh, and there have been 32-bit architectures with integer and floating point multiply and divide instructions since at least as far back as the first IBM System/360s. You know, nearly 50 years ago.

• (cs) in reply to Frz
Frz:
How do you get 40.9599609375 with integer Maths? It's probably some embedded system assembler magic.
Well:
int register5 = (((register4 << 3) + (register4 << 1)) << 2) + register4;
That calculates (8*r4 + 2*r4)*4 + r4, i.e. 41*r4
register5 -= register5 >> 10;
That takes away 1/1024 of the 41*r4, leaving r5 = 1023/1024*41*r4. And 1023/1024ths of 41 is 40.9599609375.
• Walky_one (unregistered) in reply to TV John
TV John:
Since we're talking integer maths, if Reg 5 contains 41 then it will still contain 41 afterwards, not 40.9599...
Last time I did integer math I still rounded down. So I'd guess the answer should be 40.
• Walky_one (unregistered) in reply to Walky_one
Walky_one:
TV John:
Since we're talking integer maths, if Reg 5 contains 41 then it will still contain 41 afterwards, not 40.9599...
Last time I did integer math I still rounded down. So I'd guess the answer should be 40.

Or maybe not... (my mistake)

• Pascal (unregistered)

TRWTF is the parameter order: month, day, year.

• Oddie (unregistered) in reply to Hinek
Hinek:
Also:

For April, June, September, November it is not checked if register3 is >= 1.

Yes it is, it's bigger than 28 already.

• Oddie (unregistered)

I really don't see a WTF, this is a good and efficient ASM implementation of the generic date validation math for leap years, considering the limitations.

I don't see how the math has anything to do with the year being limited to 25600. Looking at the bit shifts, that would use only 21 bits, and we're told the arch is 32 bits, so that's not the issue.

I know our calendar will be off for 1 full day in some 3300 years with our current leap-year scheme... still doesn't match the 25600 number.

• (cs)

Still looking for TRWTF?

It's here: "He found it fascinating, and spent a great deal of time examining its innards."

This means that this clever bit of code was great, but not sufficiently documented to allow it to be supported if there ever turns out to be a bug or a change to the requirements.

• TV John (unregistered) in reply to Walky_one

You might do, but we're talking about register shifting here which isn't quite the same. If you start with 16 as an example, shift right once -> 8, twice -> 4, etc, until you have 1. Shift once more, or any number beyond that, and you have zero. So what we have is 41 - 0, which is still 41.

• RandomGuy (unregistered)

The mistake is that in 1582 there was no October 5th to October 14th, but there were February 29th in 1500, 1400, ...

But, of course, TRWTF is translating assembler to Java.

• eric76 (unregistered)

One approach that could be used is to use a routine that calculates the day of the year. Then for day-month-year, subtract the day of the year for 1-month-year from 1-month+1-year. If month is 12, subtract 1-month-year from 1-1-year+1.

• Hannes (unregistered) in reply to Oddie
Oddie:
Hinek:
Also:

For April, June, September, November it is not checked if register3 is >= 1.

Yes it is, it's bigger than 28 already.

I think we should introduce a check, if register3 is bigger than 1 if it is bigger than 28. Just to be sure, you know?

• Neil (unregistered) in reply to Steve The Cynic
Steve The Cynic:
None of that explains how we get to such a number
As you know, centuries are not leap years, but every 400 years is a leap year. (And then we'll probably have to make 4000 not be a leap year, but I'll ignore that for now.)

Since we can't divide by 100, we need to divide by something easier, in this case, 4096.

So we multiply the century by 40.96 (approximately, which is why the calculation doesn't work after 25699) and look to see whether it is a multiple of 4096.

• Andrew (unregistered) in reply to RandomGuy
RandomGuy:
The mistake is that in 1582 there was no October 5th to October 14th, but there were February 29th in 1500, 1400, ...
Depends on where you lived. Many people were still using the Julian calendar for centuries afterward. But whatever you do, don't go medieval on us.
• sir_unwtf (unregistered)

where is register1?

• (cs) in reply to TV John
TV John:
You might do, but we're talking about register shifting here which isn't quite the same. If you start with 16 as an example, shift right once -> 8, twice -> 4, etc, until you have 1. Shift once more, or any number beyond that, and you have zero. So what we have is 41 - 0, which is still 41.
Only if the original value of r4 was 1. Current year numbers are somewhat larger than that.
Still looking for TRWTF?

It's here: "He found it fascinating, and spent a great deal of time examining its innards."

And that's different from what we're all doing here how exactly? ;)
• Christian (unregistered)

We wanted to see the Assembler source code.... It would have been far more compact thant that bloated java code

• TV John (unregistered) in reply to DaveK
DaveK:
TV John:
You might do, but we're talking about register shifting here which isn't quite the same. If you start with 16 as an example, shift right once -> 8, twice -> 4, etc, until you have 1. Shift once more, or any number beyond that, and you have zero. So what we have is 41 - 0, which is still 41.
Only if the original value of r4 was 1. Current year numbers are somewhat larger than that.

Indeed. I was just giving an example of why you don't round down. It continues to work however large the number is.

• (cs) in reply to Andrew
Andrew:
RandomGuy:
The mistake is that in 1582 there was no October 5th to October 14th, but there were February 29th in 1500, 1400, ...
Depends on where you lived. Many people were still using the Julian calendar for centuries afterward. But whatever you do, don't go medieval on us.
Yes, indeed. England (or, rather, Great Britain) switched in 1752, others at various times before and after. Late offenders were Bulgaria (1916), Russia (early 1918) and the very last country to switch from the Julian to the Gregorian calendar, Greece (1923). Some will talk about the RoC and how one interpretation says they adopted the Gregorian calendar in 1929, but they switched from traditional Chinese calendars, not the Julian calendar. But also note that 1582 was not a mediæval date, anyway - the Middle Ages are conventionally described as running from around 450 AD to around 1450 AD.
• Hasse de great (unregistered) in reply to Andrew

There is even one occation of Feb 30th. 31st of Feb is probably a hoax, but there are some vague references.

• VAXcat (unregistered)

This reminds me of some so called programmers that I wanted to strangle on 1-Jan-2000. These cretins had written a job scheduling program that our business totally relied on, and it keeled over due to Y2K issues. I had to get up, seriously hing over, and go in a to get it back on the air. Looking at their code, the yahoos had put in lots of code to support 5 digit years, but had muffed the simple changes needed for the year 2000. These idiots thought their code was going to survive into the year 10000...

• Pista (unregistered)
It was designed to run on a 32-bit machine that lacked multiply or divide instructions

I pity the poor soul. He had to write in assembler on such a machine. Now that's TRWTF!

• Pista (unregistered)
It was designed to run on a 32-bit machine that lacked multiply or divide instructions

I pity the poor soul. He had to write in assembler on such a machine. Now that's TRWTF!

• nitePhyyre (unregistered) in reply to VAXcat
VAXcat:
This reminds me of some so called programmers that I wanted to strangle on 1-Jan-2000. These cretins had written a job scheduling program that our business totally relied on, and it keeled over due to Y2K issues. I had to get up, seriously hing over, and go in a to get it back on the air. Looking at their code, the yahoos had put in lots of code to support 5 digit years, but had muffed the simple changes needed for the year 2000. These idiots thought their code was going to survive into the year 10000...
That's a shame. If only there had been some sort of warning that these problems were coming so you could have fixed it before hand during regular, non-hungover, work hours.
• Cheap Micro (unregistered) in reply to Steve The Cynic
Steve The Cynic:
Oh, and there have been 32-bit architectures with integer and floating point multiply and divide instructions since at least as far back as the first IBM System/360s. You know, nearly 50 years ago.

Yeah, but there where a lot of other chips that didn't to save space on the die and make them attractive to the hobbyist / Micro market.

• Thierry (unregistered) in reply to Cheap Micro
Cheap Micro:
Steve The Cynic:
Oh, and there have been 32-bit architectures with integer and floating point multiply and divide instructions since at least as far back as the first IBM System/360s. You know, nearly 50 years ago.

Yeah, but there where a lot of other chips that didn't to save space on the die and make them attractive to the hobbyist / Micro market.

And on most of the others it was less costly in cpu cycles to do a bunch of sums, subs and shifts than a single division.

• TV John (unregistered) in reply to Steve The Cynic
Steve The Cynic:
Oh, and there have been 32-bit architectures with integer and floating point multiply and divide instructions since at least as far back as the first IBM System/360s. You know, nearly 50 years ago.

Yes, there were, but as others have commented it was hugely expensive in terms of CPU cycles. I used to sit with the 8086 bible in front of me working out how to shave cycles here and there. Given the sort of tasks you would typically do in assembler complex maths wasn't really required. Rather than multiply by 10, for example, it was way quicker to do bit-shift three left (multiply by 8) and two adds to get to your answer.

Bear in mind, also, the speed of processors at the time. I started off using assembler to write to the screen buffer of a Superbrain, an all-in-one pc which used a 4MHz Zilog Z80 processor. Shaving those processor cycles made a very visible difference to the speed at which that happened. Nowadays you probably wouldn't worry. Much like the profligate way data is stored: the first time I saw XML I couldn't believe the waste of space, having come from an era where you packed data in as tight as possible because all you had for storage was 720k floppy disks! Happy daze!

• (cs) in reply to Steve The Cynic
Steve The Cynic:
England (or, rather, Great Britain) switched in 1752, others at various times before and after. Late offenders were Bulgaria (1916), Russia (early 1918) and the very last country to switch from the Julian to the Gregorian calendar, Greece (1923).
This site lists Turkey as the last country switching from Julian to the Gregorian calendar in 1926/1927.
Steve The Cynic:
But also note that 1582 was not a mediæval date, anyway - the Middle Ages are conventionally described as running from around 450 AD to around 1450 AD.
There is no exact definition - these events are commonly used to define the end of the middle ages:

1453 defeat of Constantinople 1485 battle of Bosworth Field 1492 Christopher Columbus tries to outsource programming to India, but accidentally finds the first Nagesh. 1517 Programming Reformation - Richard Stallman publishes his 95 theses about the differences between cathedrals and basars and invents open source software.

Still looking for TRWTF?

It's here: "He found it fascinating, and spent a great deal of time examining its innards."

This means that this clever bit of code was great, but not sufficiently documented to allow it to be supported if there ever turns out to be a bug or a change to the requirements.

Exactly, if you have to write code that looks like a WTF at first glance because of weird scenarios, you better add comments, or it won't last long.

• (cs) in reply to Neil
Neil:
Steve The Cynic:
None of that explains how we get to such a number
As you know, centuries are not leap years, but every 400 years is a leap year. (And then we'll probably have to make 4000 not be a leap year, but I'll ignore that for now.)

Since we can't divide by 100, we need to divide by something easier, in this case, 4096.

So we multiply the century by 40.96 (approximately, which is why the calculation doesn't work after 25699) and look to see whether it is a multiple of 4096.

This comment should be highlighted in blue.

• OldCoder (unregistered) in reply to Hannes
Hannes:
Oddie:
Hinek:
Also:

For April, June, September, November it is not checked if register3 is >= 1.

Yes it is, it's bigger than 28 already.

I think we should introduce a check, if register3 is bigger than 1 if it is bigger than 28. Just to be sure, you know?

The original comment is from someone whose first language isn't English (of any sort).

What he is saying is that, for months with 30 days, there is no test that the day number is >= 1. That test should probably have been done before the case statement, not after.

• Anonymous (unregistered)

TRWTF is introducing r1 and r2 outta nowhere

• noland (unregistered) in reply to OldCoder

The branch for evaluating April, June, etc is only visited, if the day is gte 29. Else, there is a test for the day being at least 1. So there is no edge-case:

```if (register3 >= 29) {
if (is_30days_month()) return register3 <= 30;
if (is_feb()) return is_leap() && register3 <= 29;
}
return register3 >= 1 && register3 <= 31;```
or a change to the requirements.
You mean, like the next time we switch to a different calendar? ;)
• Steve Wahl (unregistered) in reply to OldCoder
OldCoder:
What he is saying is that, for months with 30 days, there is no test that the day number is >= 1. That test should probably have been done *before* the case statement, not after.

Everyone who thinks there's no test for >= 1 for 30 day months is misreading the code.

The switch statement is only reached when the day is 29 or greater. All others fall through to the final check at the bottom:

return r3 >= 1 && r3 <= 31;

The comment above it, "// 31-day months," is misleading and was obviously added in the java translation. It should say "31 day months and all days < 29".

• noland (unregistered) in reply to Steve Wahl
Steve Wahl:
OldCoder:
What he is saying is that, for months with 30 days, there is no test that the day number is >= 1. That test should probably have been done *before* the case statement, not after.

Everyone who thinks there's no test for >= 1 for 30 day months is misreading the code.

The switch statement is only reached when the day is 29 or greater. All others fall through to the final check at the bottom:

return r3 >= 1 && r3 <= 31;

The comment above it, "// 31-day months," is misleading and was obviously added in the java translation. It should say "31 day months and all days < 29".

Moreover the use of ">=" and "<=" could add some to the confusion. In the original assembler code this was probably a subtraction and a test for the sign-bit. Actually, the branch on the "greater-or-equal-29" condition is exclusively a test for the date being 29 or 30 for April, June, etc, and for being 29 for Feb, if the year is a leap year. All other dates are handled in the last condition, "r3 >= 1 && r3 <= 31".
• (cs)
Now nobody expects you to read through a lengthy assembler listing
Maybe, but some of us actually understand assembly language and would have preferred to see the actual code in addition to the high-level translation...
• (cs)

Now that the this has been batted around a bit, I'll reveal the rest of the story...

The aim is to multiply the year by 40.96 (the 40.9599609375 is due to limitations of the math used and the multiply depends on rounding to get the correct 40.96 multiple).

And the point of 40.96? That scales the centuries to be multiples of 4096. So 1900 becomes 77,824 and 2000 becomes 81,920. And the point of that? Well, you have to look at it in binary....

Let's look at 2000 first; 81920 in binary is:

0000 0000 0000 0001 0100 0000 0000 0000

The 12 binary zeros on the right reveal that this year is divisible by 4,096, which is 40.96 * 100, so that's an even century.

But if you're astute, you will note that there are 14 zeros on the end. That means that the number is divisible by 16,384, which is 4 * 4096, which is 40.96 * 100 * 4 or every 400 years.

Got that?

Okay, now look at 77,824:

0000 0000 0000 0001 0011 0000 0000 0000

There are 12 binary zeros on the end, so this is a century year, but there are not 14 binary zeros on the end, so this is not a year divisible by 400.

I looked at this off and on for about a week before light finally dawned. And I no longer have the actual code; but the concept will stick with me forever.

• jfh (unregistered) in reply to DaveK
DaveK:
And now for something completely different: the Y25k7 problem.

When I once told a client that 3rd party software we were using had a Y10K problem (hard-coded 4-digit years), he answered: "I hope this becomes a problem."

• ¯\(°_o)/¯ I DUNNO LOL (unregistered)

Looks like widdle baby threw a tantrum and pooped his pants all over the thread.

• noland (unregistered)

Wow, someone (ssddfdfsdf) has discovered ^A (select all)! Congrats, and welcome to the community of computer users!

• (cs) in reply to Quietust
Quietust:
Now nobody expects you to read through a lengthy assembler listing
Maybe, but some of us actually understand assembly language and would have preferred to see the actual code in addition to the high-level translation...
+1