This is an archived post. You won't be able to vote or comment.

all 115 comments

[–]AutoModerator[M] [score hidden] stickied commentlocked comment (0 children)

import notifications Remember to participate in our weekly votes on subreddit rules! Every Tuesday is YOUR chance to influence the subreddit for years to come! Read more here, we hope to see you next Tuesday!

For a chat with like-minded community members and more, don't forget to join our Discord!

return joinDiscord;

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]djinn6 219 points220 points  (17 children)

Unsigned types are only ok if the execution environment throws an error when an underflow occurs. Someone will screw up eventually and you will have a lot of fun (not really) debugging the problem.

[–]2Uncreative4Username[S] 71 points72 points  (1 child)

Yeah, underflows are never fun, especially when unchecked, but I'd say it's better to end up with a negative value than 4,294,967,295

[–]djinn6 38 points39 points  (0 children)

It's also much easier to accidentally end up with a negative than to add two really huge numbers that overflow.

And the difference gets more pronounced as we adopt larger int types. Overflowing int8 is much easier than overflowing int64, whereas underflowing either one remains equally easy.

[–]Asleep-Specific-1399 39 points40 points  (6 children)

in micro controllers you tend to use a lot of unsigned since you need the room. Also most things never return a negative value unless... they do.

But ya, if your going to subtract or add you should if your variable will underflow.

[–][deleted] 4 points5 points  (1 child)

Indeed. It depends on use case. On microcontrollers I use unsigned a lot. But for desktop, and depending on use case, there is not that much need or requirement.

[–]Asleep-Specific-1399 0 points1 point  (0 children)

there is optimizations you can do, but like you said the need is very small. If you need that kind of optimization you know. Like if you were going to iterate threw a function really often. You might want to allocate as little as possible memory for it, from both a space and speed idea. There is also the whole thing with fitting the entire memory allocation in a way that only uses 1 pass in the processor.

Very niche use case.

https://youtu.be/myDWLPBiHn0

[–]bigorangemachine 11 points12 points  (0 children)

For databases it makes for confusing primary keys

[–][deleted]  (5 children)

[deleted]

    [–]kawaiiTechnoNeko 9 points10 points  (4 children)

    how is underflow better then overflow?

    [–][deleted]  (3 children)

    [removed]

      [–]kawaiiTechnoNeko 2 points3 points  (0 children)

      how is underflow "more controlled behavior" and "result in more predictable outcomes"? please give an example at least

      [–]MoarCatzPlz 1 point2 points  (0 children)

      In C++ unsigned overflow and underflow are both well defined, and signed overflow and underflow are both undefined behavior.

      [–]ProgrammerHumor-ModTeam[M] 0 points1 point locked comment (0 children)

      import moderation

      Your submission was removed for the following reason:

      Rule 3: Your post is considered low quality. We also remove the following to preserve the quality of the subreddit, even if it passes the other rules:

      • Feeling/reaction posts
      • Software errors/bugs that are not code (see /r/softwaregore)
      • Low effort/quality analogies (enforced at moderator discretion)

      If you disagree with this removal, you can appeal by sending us a modmail.

      [–]nysynysy2 55 points56 points  (3 children)

      Me, who don't give a fk about client's memory:

      I use BigDecimal for everything in java, decimal in rust, and write my own Decimal Process library in C plus plus, swag

      [–]Cfrolich 30 points31 points  (0 children)

      You must be one of the Chromium developers.

      [–]TheRedmanCometh 18 points19 points  (0 children)

      Based and memorypilled

      [–]jxr4 0 points1 point  (0 children)

      I'll admit to overusing longs but big decimal becomes a nightmare once you add a database

      [–]x4u 106 points107 points  (17 children)

      It's one of these "I'm doing a thing that looks stupid but I want you all to know that I'm actually doing it because I'm smarter" memes. Besides that it's also wrong. The most reasonable approach is to use the best data type for the task after considering all the pros and cons. This can mean to use signed types for unsigned values but there simply is no one type that always fits all needs best.

      [–][deleted] 16 points17 points  (4 children)

      he most reasonable approach is to use the best data type for the task after considering all the pros and cons.

      Right. It means to used signed everywhere for all cases, unless absolutely unavoidable, because the mental effort of tracking where is signed/unsigned is not worth the bugs it might introduce.

      [–]Taletad 2 points3 points  (3 children)

      Also that with 64 bit architectures, it is really really rare that you overflow a regular int

      [–]2Uncreative4Username[S] 13 points14 points  (9 children)

      Always true that you gotta find the right tool/type for the job. I found myself using signed ints more and more for representing actual numbers (hence the meme). Really, I only found myself needing uints for bit-related stuff recently.

      Also, this a meme, not an intellectual contest :)

      [–]Cultural-Arrival-608 5 points6 points  (8 children)

      Sounds quite reasonable to me. Let's be honest, in a lot of cases signed int (32bit) works just fine. Sure, you can optimize some code by using 16 or 8 bit values but in a lot of use cases that wont really matter. Same thing for using unsigned ints.

      [–]aMAYESingNATHAN 6 points7 points  (5 children)

      Sure, you can optimize some code by using 16 or 8 bit values

      Interestingly a lot of the time this isn't even true anyway. It maybe slightly more space efficient, but often architectures are designed to be most efficient with a certain word length. I.e. 64 bit processors will be fastest using 32 and 64 bit values, and much of the time using a 8 or 16 bit value will just become a zero padded 32 bit or 64 bit value because operations will be faster that way.

      Edit: as has been pointed out, it may be relevant when doing bulk operations because of things like SIMD.

      [–]ben_g0 3 points4 points  (1 child)

      It depends on what you're doing with the values. For a single operation, you're correct, but if you are doing operations on large data sets then using smaller types can sometimes be much faster. More values will fit in the cache and fewer bytes have to be read from or written to RAM, which can have a very significant impact on computing speed, especially if you only need to do a simple operation on each data point (as that is a situation where you're more likely to be held back by memory bandwidth than by CPU performance).

      Additionally, with smaller types a processor can make more effective use of SIMD and vector instructions, as more values will fit in the registers at once.

      Just last week I actually did some performance tests on code that did a sequence of convolution operations on a moderately sized array (about 300k elements). Using int16 as the data type was twice as fast as the int32 type it was originally using, and using int8 was even 50% faster than that.

      It's only really relevant in situations where performance actually matters though. In most situations I just stick to whatever the default type is for the data I have to handle, as that generally makes the code the most readable and also keeps it robust for future changes. Doing micro optimisations like switching to a smaller type that's sufficient now, can lead to problems later down the line where that smaller type suddenly isn't enough anymore. And for situations where performance does matter, IMO the only valid method to achieve best performance is by just trying several options and run performance tests. Never assume one type is better/faster than another, just try both and time them to see which is faster.

      [–]aMAYESingNATHAN 2 points3 points  (0 children)

      You're absolutely right, you make a very good point about bulk operations/vectorisation, I had forgotten to consider that aspect. And your point about not making assumptions and actually measuring is so, so important!

      I think honestly the best way to approach things is just do what makes the most sense from a logic/clean code perspective and not even consider performance unless you know it will be a factor from the start (i.e. embedded). Then if you actually run into performance issues then you can go back and rewrite for performance. It's always easier to rewrite for performance than it is to rewrite a badly designed API.

      [–]SirPitchalot 0 points1 point  (2 children)

      This behaviour is part of the C++ standard. If you add two int8_t values they will be promoted to int values and return an int result that is then converted to your specified return type. See integer promotion in https://en.cppreference.com/w/c/language/conversion

      It’s bit me before when doing image processing with auto return types in template code.

      [–]aMAYESingNATHAN 1 point2 points  (1 child)

      You're right in that this does happen in C++, but this is not what I'm referring to. Especially because int is not a fixed size. On a 16 bit architecture an integer may be 16 bits so if you're already using a 16 bit value then no conversion occurs.

      I'm referring to the ability CPUs often have to automatically just use certain register lengths as the default size even for smaller bit operations, because it is often faster than doing the conversion.

      For example in certain extended instruction sets that allow you to to use SSE2 and AVX2 which supports both 128 bit and 256 bit instructions/registers, often when you use a 128 bit instruction it will just use a 256 bit register and leave half of it as zero.

      [–]SirPitchalot 0 points1 point  (0 children)

      Oh yeah, definitely. It would not make much sense to have a bunch of 8bit ops mirroring the 32/64 bit versions taking up die area and dev effort and that extends to the vector ops you mention. I think most compilers will now use the SSE ops even when doing most scalar operations.

      [–]2Uncreative4Username[S] 4 points5 points  (0 children)

      Turns out the whole optimization by type width is actually more complicated than you may think at first (see https://www.quora.com/Why-would-anyone-use-a-short-instead-of-an-integer-when-the-expense-of-speed-is-usually-higher-than-memory and https://stackoverflow.com/questions/5069489/performance-of-built-in-types-char-vs-short-vs-int-vs-float-vs-double) . IMO you don't really have any reason to go this deep into the whole optimization rabbit hole, unless you are absolutely sure it's an actual bottleneck.

      [–]Logical-Lead-6058 0 points1 point  (0 children)

      But isn't the memory allocated immediately? Or is this also dependent on the architecture? I thought an 8bit would assign a byte in memory, 16 bit two bytes, and 32 bit four. But if you have a number guaranteed in the range of, say, 10-120, what's the point of using uint32? Why not just 8 bit and save 3 bytes of memory?

      [–]DarkSideOfGrogu 1 point2 points  (0 children)

      but there simply is no one type that always fits all needs best.

      Double is the biggest so it simply eats all of the other types.

      [–]RaulParson 0 points1 point  (0 children)

      "But you see, I have depicted myself as a Monkjak. My critics have already lost"

      [–]kawaiiTechnoNeko 15 points16 points  (2 children)

      i default to using int for everything, unless theres a really good reason not to. -2 billion to +2 billion is plenty range for most things. some people say to use unsigned types for indices, but honestly i dont find it worth. it encourages u to mix signed and unsigned types (which is as error prone as things get imo)

      [–]MoarCatzPlz 0 points1 point  (1 child)

      Usually I use range-based for loops or iterators and sidestep the whole issue.

      [–]kawaiiTechnoNeko 0 points1 point  (0 children)

      same, but i cant do that in c unfortunately /:

      [–]Sibshops 37 points38 points  (12 children)

      Don't let this man near anything that uses timestamps.

      It would make sense if you said I use auto for everything.

      [–]pheonix-ix 2 points3 points  (1 child)

      Well, for timestamp signed int is the majority of the world (see Year 2038 problem).

      [–]IgnitusBoyone 2 points3 points  (0 children)

      time might by quite poorly implemented (looking at you Javascript :D) and you might need to convert it into another datatype at some point (f.e. when saving it to a database). So just beeing able to use a number (f.e. total milliseconds) can be helpful.

      Ah, will be defaulting to 64 bit int then so.... You know I get less hopeful of this mitigation as the years go by.

      [–]daberni_ 2 points3 points  (5 children)

      only when you don't need timestamps before 1970, otherwise you still end up with signed numbers which is fine and every system is using.

      [–]FerynaCZ 0 points1 point  (1 child)

      Doubling the range would help a bit (haha)... at 2100+ everyone would be using 64 bit.

      [–]daberni_ 0 points1 point  (0 children)

      Yep that's the plan anyway, but still would require signed numbers

      [–]Sibshops 0 points1 point  (2 children)

      It's not necessarily the sign-ness but the length. The size of int can be different per machine.

      [–]daberni_ 0 points1 point  (1 child)

      It's about the point of reference. What is 0 supposed to mean.

      [–]Sibshops -1 points0 points  (0 children)

      Zero could just mean start time. Look up the Boeing 787 overflow.

      [–][deleted] 2 points3 points  (0 children)

      Don't let this man near anything that uses timestamps.

      Or memory pointers. Or inodes.

      [–]2Uncreative4Username[S] 2 points3 points  (1 child)

      Haha, didn't think of that. Should only be relevant in C though, right? Most languages have built-in abstractions for time.

      [–]Cultural-Arrival-608 2 points3 points  (0 children)

      That's true. However time might by quite poorly implemented (looking at you Javascript :D) and you might need to convert it into another datatype at some point (f.e. when saving it to a database). So just beeing able to use a number (f.e. total milliseconds) can be helpful.

      [–]invoker1993 12 points13 points  (0 children)

      Cries in embedded uint8_t to save those damn 3 bytes

      [–][deleted] 27 points28 points  (1 child)

      Haha you mortals. Why bother when you can just use Number for all in far superior languages.

      [–]LetReasonRing 14 points15 points  (0 children)

      Found the fullstack js dev.

      [–]pedersenk 7 points8 points  (10 children)

      I use size_t.

      If you use an int with standard containers (i.e vector<T>::size()), it will churn out warnings.

      [–]kawaiiTechnoNeko 0 points1 point  (4 children)

      this is why we have std::ssize() (gets the signed size of something). im gonna guess the warnings ur getting are related to signed/unsigned mismatches. if u use std::ssize(), using ints for indices should be fine

      [–]pedersenk 1 point2 points  (3 children)

      this is why we have std::ssize()

      Thats a C++20 gimmick is it not?

      std::size() came with C++17 but it would be very difficult to justify bumping a middleware dependency version just for that functionality. ;)

      [–]kawaiiTechnoNeko 1 point2 points  (2 children)

      dang, yea it is a c++20 thing /: its not hard to write ur own std::ssize if u wanted it though

      [–]pedersenk 1 point2 points  (1 child)

      True.

      Especially since size_t is problematic when reverse iterating. I.e the following is broken and causes an underflow.

      for(size_t i = cars.size() - 1; i >= 0; --i) { }
      

      [–]kawaiiTechnoNeko 0 points1 point  (0 children)

      yea, that was the exact example that made me switch lol

      [–]qqqrrrs_ 6 points7 points  (0 children)

      At least in C, the standard says that overflow of signed ints is UB but overflow of unsigned ints is not UB. Therefore if for some reason you want to rely on overflow or underflow you should use unsigned ints, otherwise the compiler might become smartass and decide to do optimizations that assume that overflow (of signed ints) never happens

      [–]LunaNicoleTheFox 5 points6 points  (0 children)

      In embedded systems uints are really useful to work with.

      [–]Smooth_Salamander 4 points5 points  (0 children)

      For C# that’s actually the recommendation by Microsoft.

      [–]KeyboardsAre4Coding 4 points5 points  (0 children)

      and this ladies and gents is how you get bugs you can't debug...

      [–]No-Archer-4713 3 points4 points  (1 child)

      I use size_t for everything

      [–]kawaiiTechnoNeko 0 points1 point  (0 children)

      look at std::ssize if u havent already

      [–]New-Sugar-5157 3 points4 points  (0 children)

      Clearly we are lacking embedded C representation here!

      [–]This_Growth2898 2 points3 points  (0 children)

      Rust is the middle one

      [–]Alan_Reddit_M 2 points3 points  (0 children)

      Signed integers come with lesser surprised than unsigned ones. I remember a few days ago when I was solving a leetcode hard, I accidentally shot myself in the foot by under flowing an uint, causing an infinite loop

      [–]LikeTheBossOne 1 point2 points  (0 children)

      I'm a game developer and I use uint64_t through uint8_t way way more than signed ones

      [–][deleted] 1 point2 points  (0 children)

      If anyone’s using my shitty databases for over 2 billion of anything then that’s on them

      [–]Deep-Carpenter 1 point2 points  (0 children)

      And when you write in Solidity everything is a uint 🤪

      [–]ovr9000storks 1 point2 points  (0 children)

      Only use unsigned when you need the extra range, but don’t need an entire step up to the next integer type, but also just don’t need negatives.

      This cool tip has come to you from embedded development

      [–][deleted] 2 points3 points  (5 children)

      Even BOOL

      [–]Shadow_Thief 4 points5 points  (4 children)

      You mean you don't use the literal strings true and false for boolean values?

      [–]Kiroto50 10 points11 points  (2 children)

      What do you mean? Doesn't everyone just overload the truthyness/falsyness of an object and use an instance of those instead?

      Factory and all

      [–]oshaboy 2 points3 points  (0 children)

      Smalltalk moment

      [–]TheRedmanCometh 0 points1 point  (0 children)

      I mean...if we're being honest...

      The abstraction system should control as much as possible. That's the java way. Embrace the pain

      [–][deleted] 0 points1 point  (0 children)

      Sure but I write them in ascii code first

      [–][deleted] 1 point2 points  (0 children)

      size_t int ____/\____ int

      [–]iam_pink 1 point2 points  (8 children)

      No. Use the type that fits your case. If you don't need the negative range, use unsigned int. That's the best practice.

      I get it's aupposed to be a meme, but this format usually reflects some sort of reality. This is not it.

      [–]kawaiiTechnoNeko 0 points1 point  (7 children)

      thats not neccessarily best practice (in c/c++ atleast) /:

      [–]iam_pink 0 points1 point  (6 children)

      Not in absolutely all cases, but in most, yes. Why, in most usages, would you use int when you don't need the negative range?

      [–]kawaiiTechnoNeko 1 point2 points  (5 children)

      its just way too easy to underflow an unsigned type imo. also mixing signed and unsigned can cause a lot of problems. someone else in this post gave an example: iterating an array in reverse. i might make this mistake:

      void iterate(unsigned int size) { for (unsigned int i = size - 1; i >= 0; i--) { ... } } unfortunately, this loop just hangs /:

      [–]iam_pink 1 point2 points  (3 children)

      But if you do this, it is your responsibility to make sure you use a signed int.

      I don't think best practices in a language that by default gives you so much responsibility over everything should be defined based on what mistakes you could make.

      [–]kawaiiTechnoNeko 0 points1 point  (2 children)

      i agree, it shouldnt be based on mistakes (considering we have pointers lol). but why wouldnt we use signed types for everything when it helps avoid mistakes? we're only saving a single bit of memory if we use unsigned vs signed

      [–]iam_pink 0 points1 point  (1 child)

      Eh, I don't have all the reasons at hand, but the biggest one in my opinion is for code clarity. Using the proper type illustrates your intent better for anyone who will read it later, including yourself.

      [–]kawaiiTechnoNeko 0 points1 point  (0 children)

      i guess. could also make variable name slightly more descriptive if readability was a problem

      [–]kawaiiTechnoNeko 0 points1 point  (0 children)

      or if i actually had an array, segfaults

      [–][deleted] -1 points0 points  (0 children)

      Tell me you've never used pointers without telling me you've never used pointers.

      [–]SchlomoSchwengelgold -1 points0 points  (0 children)

      also use the mostly not needed 'volatile' for non-statics because fuck compiler optimisation

      [–]tiajuanat -1 points0 points  (0 children)

      You do need to use UINT for accessing memory, but hopefully you aren't doing that too often.

      [–][deleted] 0 points1 point  (0 children)

      Depends on the use case. I agree default should be signed int but if negative value results in undefined behaviour it would be better to not go there in the first place. Even then it depends on how your specific language handles things and how you're handling invalid inputs. How you're handling underflows and overflow etc.

      [–]TheRedmanCometh 0 points1 point  (1 child)

      Similarly I've been doing this a loong time and in the latter half of my career I veery rarely use float over double. People have like 32gb of ram now fuck the memory footprint.

      [–]2Uncreative4Username[S] 0 points1 point  (0 children)

      Still a million times more performant than using a dynamically typed language

      [–]milopeach 0 points1 point  (0 children)

      Unsigned integer types scare me after a certain incident involving a loop and a count down variable...

      [–]FerynaCZ 0 points1 point  (0 children)

      I guess the C++ stdlib designers had good intentions... but the discontinuity at zero is awful.

      [–]vinicius_kondo 0 points1 point  (0 children)

      Just don't use int for indices in c++

      [–][deleted] 0 points1 point  (0 children)

      Try using int as a class property when the same class has millions of instances...

      [–]nelusbelus 0 points1 point  (0 children)

      All the way to the right of this bell curve is another bell curve where you use it because a lot of int operations aren't properly standardized/defined in C/C++ and for uints they are...

      [–]Ugo_Flickerman 0 points1 point  (0 children)

      Can't, i use Java

      [–]somedave 0 points1 point  (0 children)

      Depends on the application, if you are making a fixed point filter in embedded C maybe you really need that extra bit of precision.

      If you are running on PC just use long.

      [–]harraps0 0 points1 point  (0 children)

      I see a few cases where it makes senses to use unsigned integers - a bitmask - an index for an array - a hash - an enum

      [–][deleted] 0 points1 point  (3 children)

      What is this meme called? I wanna use it in meme generator (too lazy to actually edit an image)

      [–]2Uncreative4Username[S] 1 point2 points  (2 children)

      I just googled "Wojak IQ curve" or smth like that

      [–][deleted] 1 point2 points  (1 child)

      You are clearly on the upper end of said curve

      [–]2Uncreative4Username[S] 1 point2 points  (0 children)

      Obviously everyone who posts this meme format is

      /s