|
Pure dates are generally not a problem, as long as you (or the coder from whose output you are getting the date) don't/didn't do something very stupid. Even here, however, timezone issues can cause problems. I once had an issue which resulted from the author of the firmware of a device I was getting data from recording what should have been a pure (midnight) datestamp as the corresponding local datetime. Since I am in the GMT-5 timezone, midnight on April 4 became 7 pm on April 3!
Trying to compare datetimes for simultaneity, however, is almost always a severe PITA, when the source clocks are not both perfectly synchronized and using the same basic internal representation for clock time.
|
|
|
|
|
Storing dates in UTC internally solves almost all issues. Not all of them, but almost all. Comparing time stamps in milliseconds for equality may work or may fail, depending on the context. In a scientific context, all clocks involved are precise down to milliseconds at worst and way more precise at best. That, and time differences of a few milliseconds make huge differences.
But it all boils down to context. But yeah, I've seen some very stupid date handling myself. My point is, while comparing floats for equality is a horrible idea by default and always, comparing dates for equality may work very well depending on the circumstances. Well, that and dates are like encryption, there's heaps of way to get it wrong, many of them very subtile but still destructive and only a few (of not only one) ways to get it right.
|
|
|
|
|
- How would I explain this code to the cleaning personnel
- If you can explain how it's done, you can script it
|
|
|
|
|
I used to let them test my stuff. They were really good at finding bugs and breaking stuff because they did unexpected things which was exactly what I wanted.
"They have a consciousness, they have a life, they have a soul! Damn you! Let the rabbits wear glasses! Save our brothers! Can I get an amen?"
|
|
|
|
|
Another reason to avoid "Yoda conditionals":
if (10 > 5)
if (10.CompareTo(5) > 0)
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
one of those looks like a bug.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
My mantra is "Dammit! Dammit! Dammit!".
Way back when learning C, to help me remember to add the extra equal sign when writing a conditional statement as opposed to an assignment I would say "Equals to" and press the equal sign == for each word and to say "Equals" and only hit the equal = sign once for assigning a value.
It was broke, so I fixed it.
|
|
|
|
|
One of the reasons that I miss Pascal. Both the double == and the paretheses requirements.
|
|
|
|
|
"Developers may come and go, but bugs will stay forever"
|
|
|
|
|
Mine applies to date comparisons.
"Later is greater"
Steady Eddie (for those that never saw "The Hustler")
|
|
|
|
|
"Check the plugs."
Plugs being any of the following:
- connection strings
- method parameters
- app settings
"F5 and pray"
Let-er rip and see what happens. Don't be afraid. 😁 I usually say a short prayer as my coffee is compiling / starting up.
|
|
|
|
|
Don't solve problems which don't actually exist.
Like anything, this must be used in moderation. If there's a good, easy-to-understand abstraction you can implement which isn't required today , but you can see an obvious business case for it coming, go ahead.
At the same time, be careful of overengineering just because you thought of some possible, but unlikely, abstraction.
Design your code in such a way that can be implemented when the problem actually becomes something to solve.
|
|
|
|
|
I have worked in the automation systems business for a long, long time and I found it is almost impossible to over-engineer things there. I wrote almost only because I haven't seen it happen yet.
"They have a consciousness, they have a life, they have a soul! Damn you! Let the rabbits wear glasses! Save our brothers! Can I get an amen?"
|
|
|
|
|
Well, I more or less always knew that my greatest weakness was my tendency to want certain sections of my code to be bug free. That kind of code-blindness causes extra work when debugging. Through the years I have advanced to the point where I can realize now that I write loads of lines of bugs encapsulating some small bits of perfectly functional code. I'm actually a decent debugger now, I'm still working on the programming part.
|
|
|
|
|
AnotherKen wrote: I'm actually a decent debugger now, I'm still working on the programming part.
Ha! relatable content
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
The REALLY big mistake is that the C# designers carried forward the old C way of reporting something non-numeric as if it were a numeric. IT IS NOT!
The value of comparing A with B is either "A is less", "They are equal", or "B is less", NOT -1, 0 or 1. C# did abandon pointers as integers - "if (pointer)" is not valid; you must test "if (pointer != null)". They should have completed the job!
Every now and then I get so frustrated over this that I write a thin skin for the comparisons, casting those inappropriate integers into an enum. But C# doesn't really treat enums as a proper type, more as synonyms for integers, so it really doesn't do it; it just reduces my frustration to a managable level.
|
|
|
|
|
The problem with that is they may not be 1, 0 or -1.
Any positive value and 1 are going to have to be treated the same, and the same goes for the negative values - they're all -1, basically.
But other than that, yeah.
Although hate enums, because .NET made them slow. I still use them, but they make me frustrated.
So usually in my classes where I don't want to burn extra clocks like my pull parsers I use an int to keep state, and cast it to an enum before the user of my code touches is.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
honey the codewitch wrote: Although hate enums, because .NET made them slow. I still use them, but they make me frustrated. The very first compiler I dug into was the Pascal P4 compiler - those who think "open source" is something that came with Linux are completely wrong. Pascal provides enums as first class types, not something derived from integer.
The compiler source showed very clearly how the compiler treats enums just like integers; it just doesn't mix the two types up, it doensn't allow you to use them interchangably. It is like having intTypeA and intTypeB which are 100% incompatible. If you do casting to (or from) int, it is a pure compile-time thing: It shortcuts the error handling reporting that the types are incompatible. There is nothing that causes more instructions to be executed when you use enums rather than int - not even when you do casting. Why would there be? Why should .net make them slower?
If you have full enum implementation (like that of Pascal) and make more use of it, then there may of course be a few more instructions generated. E.g. if you have a 12-value enum from janurary to december, and define an array with indexes from april to august, then the runtime code must skew the actual index values so that an "april" index is mapped to the base adress of the array, not three elements higher. Index values must be checked against the array declaration: january to march and september to december must generate an exception. But that is extended functionality - if you want that with integer indexes, and the same checking, you would generate a lot more code writing the index scewing and testing as explicit C statements.
Maybe the current C# .net compiler is not doing things "properly" - similar to that Pascal compiler written in the early 1970s. I guess it could. I see no reason why it should be able to, nothing in semantics of C# "semi-enums" making it more difficult that Pascal's full enum implemnentation.
|
|
|
|
|
It depends on what you do with them, but casting them back and forth to int requires a CLI check, i think maybe for invalid values.
Ints don't require that.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
Casting is not something you do very often - unless you continue to think of enums as just names for ints, so you continue to mix the two types up. So it shouldn't be essential (or even noticable) to system performance.
In many cases, the compiler can suppress the runtime check, e.g. for int literals, or when a simple flow analysis reveals that an int variable couldn't possibly be outside the enum range (or most certainly would be outside, in which case the compiler should barf).
For enum-to-int casts, there should be very little need for runtime checks - very few systems define more than 32K values for one enum type, and very little code nowadays use ints of less than 16 bits. Especially: In contexts where 8 bit ints are relevant, you very rarely see huge enum definitions with more than 128 alternatives (or 256 for uint8).
If you declare enums by forcing the internal representation to be given by the bit pattern of some int value, then you show that you do not recognize enums as a distinct type. Forcing the internal representation is as bad for enums as it would be to force a pointer or float to a specific bit pattern given by the representation of a specific integer literal. You shouldn't do that. Even assuming that enum values form a dense sequence from 0000 and upwards is on the edge - they are not ints, and you cannot assume any given similarity in int and enum implementation. Really, int/enum casts are as meaningless as int/pointer casts; we have casts only because lots of C programmers can't stop thinking of them as "just a little different ints".
Even for ints, the compiler should generate code that verifies that e.g. an int32 cast to an int16 is within the int16 range. Maybe the instruction set provides some harware support, creating an interrupt if not. Support may be available even for enum use: The last machine I programmed in assembly had a four operand instruction "LoadIndex register, value, min, max": If "value" was not in the range from min to max, an "illegal index" interrupt was generated. The Pascal compiler used this instruction for int-to-enum casts, specifying the first and last permitted enum value. (In Pascal, it is not given that "min" is zero; e.g. if an array is indexed from may to september.) I haven't spent time on learning the .net "instruction set", and don't know if it has something similar. But since it does index checks, I'd exepect it to.
|
|
|
|
|
Member 7989122 wrote: In many cases, the compiler can suppress the runtime check, e.g. for int literals,
Plenty of developers overestimate the compilers they provide with .NET. They don't typically do optimizations like that. (Although in .NET you can explicitly turn overflow checks on and off on a per cast basis in your code)
Experience has shown me time and again, when it comes to .NET, if there's any doubt about whether or not the compiler will optimize something, no matter how obvious, assume it won't.
You're far more likely to be right than wrong that way.
Spend enough time decompiling .NET asms and you learn the hard way to optimize your own code.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
dotNET is neither a compiler, an instruction set nor a machine architecture. (For the last one: Contrary to e.g. P4 or JVM.) It is like the specification of the interface in the gcc compiler suite between the (programming language dependent) front end and the (cpu architecture dependent) back end. "Like" is not in a "somewhat simillar to, in a rough sense", but rather like "an alternative competing directly with the gcc intermediate format for solving exactly the same set of problems". The backend exists in .net exactly as in gcc: CPU architecture dependent code is generated when the code is first loaded into the machine, and cached for later use.
Do you ever "blame" the intermedate gcc format for front ends that doesn't optimize the code much? That's what you do with dotNET. Put the blame where it deserves to be put.
I haven't studied the source code of a single compiler for the intermediate code of neither gcc nor dotNET. Maybe they don't do even the very simplest optimizations. Why not? Most techniques have been known for more than fifty years. I may suggest that they make a tradeoff: Compile time is far more essential with an interactive IDE than in the days when you submitted a card deck for compilation and picked up the compiler listing hours earlier. So we can't spend time on optimizations while the developer is impatiently twiddeling their thumbs waiting for the compilation to complete. Execution time is far less essential today - CPUs are fast enough! Optimizing for space is almost meaningless: Adding another 16 GiByte of RAM costs almost nothing, so why delay compilation to avoid that?
To some degree, they are right. And also: Modern pipelined CPUs reduces drastically the benefit of shaving off a few instructions from a linear sequence, compared to a hardcoded RISC CPU where every instruction (ideally) requires one clock cycle. Some of the old optimization techniques can safely be left out, as they have no measurable effect at all, given today's hardware.
Example, although not from compilers: Remember the "interleaving factor" when formatting DOS disks? If logical disk blocks 0, 1, 2... were physically laid out at sectors 0, 2, 4..., then you could read an entire track in only two disk revolutions. If you laid them out at sectors 0, 1, 2..., after reading sector 0, sector 1 passed the disk head while the controller was still stuffing away the data from sector 0. So it had to wait until sector 1 came around next time, a full revolution later. Reading an entire track, when interleaving was not applied, could require as many disk revolutions as the track had sectors. It must be 25+ years since the retirement of the last disk benefiting from interleaving.
Actually, Univac 1100-series compilers used a similar interleaving for data: When a word is read from memory, that entire row must be refreshed before it can be read again. So, consecutive locations were allocated cyclically in a set of rows: If you traverse an array sequentially (which is quite common), you skip from row to row, and when you cycle back to the first one, it has had plenty of time to refresh. With three cache levels in the CPU, and integral refresh logic, such optimization in the compiler is meaningless.
I did quite a lot of timing, from a programer's point of view, when I was working for a company making their own CPUs (this was in the "supermini" era), talking directly with the CPU designers. That's when I first learned the unimportance of the length of linear sequences, the effects of pipelining and interrups invalidating the pipeline. I also saw them remove a handful "optimizing" hardware features: They turned out to have microscopic effect but made the CPU significantly more complex, making it difficult to achieve a general speedup.
A more recent example: I made a small Soduko-solver to illustrate backtracking for a colleague, counting how many times I try to place a digit, testing if it is viable. The loop contains 13 C# statements: 3 if-statements, 6 one-dim array indexing operations, 2 two-dim array indexing operations, one of two alternate function calls..., plus the for loop control. One iteration of this loop takes around 30 nanoseconds. How much below 30 ns could I squeeze it by hand optimizing those 13 statements? Not that much. The worst puzzle I have found, requiring more than 57 million test loop iterations, complete in 1.8 seconds; there is no hard "user level" requirement for further optimzation!
So: I rarely study the instruction sequence generated, claiming that "I could have removed four instructions from that linear sequence". Rather, I do timing at application level, to see the real improvements in execution speed. Often, they are not possible to measure. Sometimes they are negative. Sometimes I am tempted to look at the generated code to see how it can be possible to run those 13 sudoko-solver C# statements in 30 nanoseconds...
|
|
|
|
|
I didn't say it was. I'm well aware of .NET and what it is and isn't. I worked on VStudio Whidbey, FFS.
Member 7989122 wrote: Do you ever "blame" the intermedate gcc format for front ends that doesn't optimize the code much?
This isn't about blame. This is about stating a fact. None of the compilers shipped with any flavor of .NET that targets any flavor of the CLI optimizes very much, if at all. And when one writes code it pays to know what the compiler is actually doing with it, lest someone make some bad design decisions (like trying to use enums for state machine internals)
It's all punted to the JIT, and the JIT just doesn't optimize much.
You can wrap a response to that in as many words as you like, but it doesn't change the facts on the ground.
And it won't get me to start using enums in my state machine code internals.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
If it ain't broke, fix it 'til it is.
Oh, hang on - save your ass, you say?
|
|
|
|
|
Don't return null . Throw an exception instead.
Removes need to null check everything.
Hopefully give a more meaningful error when a problem occurs.
|
|
|
|
|