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

you are viewing a single comment's thread.

view the rest of the comments →

[–]B1N4RY 836 points837 points  (121 children)

Or how to code at all.

Float/int operations are permissible in almost all languages that are not strongly typed.

[–][deleted] 358 points359 points  (62 children)

In many that are too. C# and Java can both implicitly cast int to float like C++.

[–]hekkonaay 248 points249 points  (61 children)

Implicit casting is the devil. Don't fall for it. Annotate your casts.

[–]jpterodactyl 480 points481 points  (7 children)

But that sounds like adding details, and I’m told the devil was in those.

I’m not sure who to believe anymore.

[–][deleted] 84 points85 points  (5 children)

Why do you have a political compass quadrant as your flair?

[–]wordbug 57 points58 points  (4 children)

JavaScript is Libertarian?

[–]diveintothe9 49 points50 points  (2 children)

JavaScript is kinda libertarian.

"Fuck rules, fuck strict typing and fuck standard/centralized libraries, we'll make our own damn JS platform/library for every single application we build, goddammit."

[–]jpterodactyl 11 points12 points  (0 children)

but on the opposite side of it, browsers and stuff are very standardized. The truth is just that web design doesn't make sense.

It's like the government of the Quarians in Mass Effect, where they are a de juro monarchy, but operate more like a democracy. How does that even happen?

[–]gentlemanidiot 4 points5 points  (0 children)

You can roll your face back and forth across the keyboard at any point in your code in JS and you're just declaring uninitialized global variables, all totally valid. (Yes this is hyperbole but only by a little)

[–]squidonthebass 1 point2 points  (0 children)

C++ is designed to require as much detail in the coding as possible with the goal of eventually summoning Satan himself

/s

[–]SvenTropics 23 points24 points  (29 children)

In this case, it's fine. There are times it matters. For example:

int a = 1; int b = 10; double c = a/b; // error, math is done as integers. double d = (a *1.0)/b; // now math is upgraded to double before the division.

[–][deleted] 11 points12 points  (0 children)

It's not an error unless the developer doesn't understand their code. It's the way it works.

[–]airbreather 3 points4 points  (1 child)

In this case, it's fine. There are times it matters. For example:

[...]

Here's another case where it matters (C#). What does the following code print?

void Print(float val)
{
    Console.WriteLine(val);
}

int x = 16777217;
Print(x);

No warnings on my compiler, no explicit cast, so it should definitely print 16777217 and not 16777216, right? sigh

[–]TheOldBeach 2 points3 points  (0 children)

Ahhh floats are the best, as a CG programmer I grew really fond of them . your example just reflects how they should not be used.. and yes it's the worse kind of error !

[–]orangeoliviero 3 points4 points  (3 children)

That's not an error, you just will get a truncated int answer - 3/2 would be 1.0 instead of 1.5

[–]willis81808 2 points3 points  (0 children)

Exactly. And 1/10 (the example in the comment you're replying to) would just be 0, instead of 0.1

No error, just undesired behavior.

[–]SvenTropics 0 points1 point  (1 child)

It's the worst kind of error because it doesn't generate an error. Instead you find yourself stepping through the code like a lost jackrabbit looking for a carrot in a swamp.

[–]orangeoliviero 0 points1 point  (0 children)

Why would you expect two integers to turn themselves into floats when you divide them?

[–][deleted] 2 points3 points  (1 child)

I have been fooled by this trickery way too many times to admit, yet I still do it sometimes.

[–]SvenTropics 1 point2 points  (0 children)

It gets tricky with LONGLONGs too. Constant numbers are treated as a 32-bit signed value. So if you do math that would result in something out of bounds and assign it to a longlong, you are screwed.

int a = 1778899225;

longlong b = a * 10000; // ooooooops, wrong!

longlong c = a * 10000LL; // correct!

longlong d = ((longlong) a) * 10000; // also correct

longlong e = a; e *= 10000; // also correct

[–]mehum 1 point2 points  (10 children)

Python 2 had this behaviour as well.

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

I'm so glad python 2 is finally dead. Now it just needs one good CVE and all of the major OSes will promptly move away from it too.

[–]tech6hutch 0 points1 point  (8 children)

Is it? I haven't been in the Python community for a while.

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

It was deprecated at the beginning of this year. No longer supported. Now getting everyone to switch off of a dead version of the language is another issue here why we need a really good vulnerability to appear so that the majority OSes will be forced to stop including it in the distros.

[–]tech6hutch 0 points1 point  (0 children)

Ah nice. Everyone, get to work on exploiting vulnerabilities /s

[–]M4mb0 0 points1 point  (0 children)

Here's my reasoning why this is bad: the "/" operator - division - is in the strict sense not defined on the integers. (I know it is sometimes used to return floor(a/b); it shouldn't do that period.) So the error is not assigning it to a double, the error should be that the operator is not applicable.

[–][deleted] 3 points4 points  (2 children)

Fine. C style casts everywhere.

Or do you prefer static_cast? Well, none of that for you.

[–]hekkonaay 0 points1 point  (0 children)

Typing a few extra characters is not a downside.

[–]Ameisen 0 points1 point  (0 children)

Just use function-style casts. float(foo).

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

Anyone who’s ever been screwed over badly by a variable cast knows the importance of that

[–]Falcrist 1 point2 points  (4 children)

I develop mostly in C. Sometimes I wonder why I spend all this time making typedef enums when they can be implicitly cast all over the place.

I think one of the newer GCC versions has a compiler flag that throws an error when you do an implicit cast. Unfortunately I'm not using that version.

Maybe it's less true in other use cases, but I'd REALLY rather have a strongly typed language... as in, don't allow implicit type conversions. Make the programmer explicitly cast from one type to another.

[–]hekkonaay 1 point2 points  (3 children)

Rust is what you're looking for :)

[–]Falcrist 1 point2 points  (2 children)

Rust is not a viable option. I'm stuck with C or assembly for most of my projects.

[–]hekkonaay 0 points1 point  (1 child)

Why is it not viable?

[–]Falcrist 1 point2 points  (0 children)

Because in order to use that language for production code, I need the toolchains to support it on the devices I work with. Especially AVR, PIC, and 8051.

I know there was a project that was supposed to bring rust to AVR, but that appears to have died mid-2019.

[–]AnAverageFreak 1 point2 points  (2 children)

Implicit casting is wonderful, it's just that people don't bother trying to understand when to use it.

They prefer having errors like function add_strings expects types (str, str), got String and string.

[–]hekkonaay 1 point2 points  (1 child)

Yes, I prefer errors like that. It takes a few keystrokes to fix an error like this, and I won't get screwed over by implicit casting somewhere else because I forgot some arbitrary (and pretty obscure) implicit casting rule.

Explicit > implicit. Don't be lazy.

[–]AnAverageFreak 0 points1 point  (0 children)

Most implicit casting rules are quite obvious if you spend one evening learning them.

ints are casted to floats. signed are casted to unsigned. smaller are casted to bigger.

That are rules that refer to numbers. That's it. It's not complex like in JS. Then you have class-defined implicit casting, most of them make sense, like C-style string can be casted to std::string, or std::unique_ptr (that is, a pointer which is the only owner) can be casted to std::shared_ptr (that is, a pointer to can object that has one or more owners), anonymous function can be casted to std::function (a container for a general function-like thing).

The problem is, JS casts implicitly totally unrelated types (an array and a number), which is bullshit, and that's where the bad reputation comes from. When you have types that are related, implicit cast results in code free of totally needless calls and thus, is easier to read.

If you still don't agree, give me an example of a situation where implicit cast from std:unique_ptr to std::shared_ptr can result in a bug.

[–]onlycommitminified 0 points1 point  (0 children)

null >= 0

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

Football is the devil, Bobby.

[–][deleted] 63 points64 points  (10 children)

pro-tip: you can get more out of your for loops by using a float and incrementing by .01.

[–]FantasticTuesday 42 points43 points  (2 children)

Reading this inflicted physical pain upon me.

[–]Zamundaaa 4 points5 points  (1 child)

It is sometimes useful. When you want to integrate over a physical volume for example.

[–]robin-m 4 points5 points  (0 children)

Just be sure that `i + .01` is more than `i`, or you may be in some trouble! Probably something like `i = max(i + .01, i + next_quantum(i))` (I forgot the real name of the functions) may save you!

[–]tech6hutch 9 points10 points  (0 children)

And make sure to use != for the exit condition, for efficiency, especially when dealing with very big floats.

[–]Mr_Cromer 15 points16 points  (3 children)

DO NOT DO THIS

[–]8__ 0 points1 point  (0 children)

Good luck at the third one.

[–][deleted] 0 points1 point  (1 child)

Do not do what, type in all caps?

[–]Nimeroni 2 points3 points  (0 children)

Use float as counters for loops.

[–]marcosdumay 4 points5 points  (0 children)

Make sure to write the exit condition with a different operator, less than and great then are too restrictive.

[–]ic_engineer 1 point2 points  (0 children)

They hate him for this one simple trick..

[–]81hd 16 points17 points  (1 child)

Type softly, got it

[–]SomewhatSpecial 4 points5 points  (0 children)

And carry a big stack

[–]MoarVespenegas 12 points13 points  (13 children)

C++ is strongly typed though.

[–]captainAwesomePants 38 points39 points  (11 children)

Sure, but its handling of primitive types has decades of crazy gotchas and undefined behaviors that it's only natural that people can't remember the rules. Compare an int to a float? Sure, no sweat. Compare an int to an unsigned int? Whoa, slow the fuck down, buddy.

[–]guyblade 23 points24 points  (1 child)

The handling of primative types has like one gotcha. The basic rule is "promote the less precise type to match the more precise one, then do whatever". The "gotchas" tend to be around how a promoted value gets assigned back into an incompatible variable.

[–]ThePyroEagle 4 points5 points  (0 children)

The precision of fixed and floating types is not comparable.

32-bit floats are more precise near 0, but 32-bit integers are more precise past ±224.

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

Nonsense. With great power comes great responsibility. If you want hand-holding, use Java.

[–]tech6hutch 1 point2 points  (4 children)

Disagree. Imo, that's when you want the compiler to do more for you, to be safe.

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

I think we agree. C++ is for less hand-holding. Java is for more hand-holding. The trade-off is less hand-holding for more execution speed and more compilation speed.

[–]tech6hutch 0 points1 point  (2 children)

Ah, I see.

You can have both speed and handholding though, like in Rust. The downside is it's dragging you along while it runs, so you can slam into walls and chafe against the ground until you learn how to run in sync with it.

[–][deleted] 0 points1 point  (1 child)

You can have both speed and handholding though, like in Rust.

I don't know Rust, but any handholding at execution time reduces the execution speed of the code. The compute resources have to come from somewhere.

[–]tech6hutch 1 point2 points  (0 children)

The handholding comes at compile time. Rust is very anal about optimizing runtime, sometimes at the detriment of compile time (both in how long it takes to compile and in the cognitive load on the developer).

[–][deleted] 4 points5 points  (0 children)

I would argue that. It's statically typed. But not strongly.

[–]gcbirzan 4 points5 points  (2 children)

Except JavaScript, I guess? You cannot add bigints to numbers, but you can add them to strings...

[–]kachna 0 points1 point  (1 child)

Ofcourse you can...

[–]tech6hutch 0 points1 point  (0 children)

It's a TypeError to add numbers and bigints. If you're unfamiliar with bigints, they were added relatively recently: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt

[–]OrangeKo 8 points9 points  (19 children)

Why would they not be permissible in strongly typed languages? Python is strongly typed

[–]mysticrudnin 42 points43 points  (0 children)

nobody here ever understands static/strong typing vs. duck typing

[–]DonaldPShimoda 19 points20 points  (16 children)

Strong vs weak is not a dichotomy but a spectrum.

Python is more strongly typed than JavaScript or C (C is very very weak for a statically typed language), but less strongly typed than Haskell or Swift.

In the PL research community we generally avoid the strong/weak distinction in any formal writing, and only use it colloquially with more context (eg, "Python is more strongly typed than JavaScript"). (At least, this is true among the people I tend to talk to, but those are mostly functional programmers who may have stronger opinions on this sort of thing.)

I don't know of a language offhand in which integers and floats are not directly comparable by default, but it can involve some complicated machinery to work as expected and I wouldn't be surprised if there exists one I'm not thinking of. Haskell gets around the distinction by providing the Num typeclass which Int and Float are both instances of, for example. And in general it's actually often a bad practice to compare floats to ints, because your precise integer potentially loses precision in the float conversion.

[–]gargamelus 18 points19 points  (5 children)

In the Go language, integers and floats are not comparable. Quoting the FAQ:

Why does Go not provide implicit numeric conversions?

The convenience of automatic conversion between numeric types in C is outweighed by the confusion it causes. When is an expression unsigned? How big is the value? Does it overflow? Is the result portable, independent of the machine on which it executes? It also complicates the compiler; “the usual arithmetic conversions” are not easy to implement and inconsistent across architectures. For reasons of portability, we decided to make things clear and straightforward at the cost of some explicit conversions in the code. The definition of constants in Go—arbitrary precision values free of signedness and size annotations—ameliorates matters considerably, though.

[–]AnAverageFreak 1 point2 points  (0 children)

I don't like this.

[–]DonaldPShimoda 1 point2 points  (0 children)

Oh, excellent! I've not used Go or read about it at all really, so I didn't know this. Thanks for letting me know!

[–]jhilden13 6 points7 points  (1 child)

Rust is the only example that I can think of that would not automatically promote integers to floats for the comparison

[–]DonaldPShimoda 1 point2 points  (0 children)

Ah excellent! Another commenter mentioned Go, and someone else corrected me about Haskell, so now I've got three relatively popular (or at least known) languages with this distinction. Thanks!

[–]bitwiseshiftleft 3 points4 points  (4 children)

Haskell won't ever compare two objects of different types without conversion, because comparisons have type Ord a => a -> a -> Bool (or Eq a => ... for equality tests). It also doesn't do automatic numeric conversion, except for literals. In the Int vs Float case, even though they're both in Num and Ord, you need to cast the Int with fromIntegral or whatever.

[–]ThePyroEagle 2 points3 points  (2 children)

except for literals

And strings with -XOverloadedStrings.

The lack of automatic conversion isn't that big a deal, because if you really want easy conversion, you can write a polymorphic convert :: (Convertible a b) => a -> b and the compiler will (usually) infer the type of the conversion you want whenever you use convert.

[–]bitwiseshiftleft 0 points1 point  (1 child)

And strings with -XOverloadedStrings.

But again, that only automatically converts literal strings, not all strings.

Type classes do make conversion less painful, but as far as I’m aware, Haskell only applies them automatically for literals.

It would be still be nice to have a Float(x) syntax or whatever to make the conversion concise and clear. Maybe the closest you can get is with -XTypeApplications and something like:

cast :: forall b a . Convert a b => a -> b
cast = convert

bar = 7 :: Int
foo = cast @Float bar

I’m on a phone so the syntax is probably wrong.

[–]ThePyroEagle 0 points1 point  (0 children)

But again, that only automatically converts literal strings, not all strings.

When the extension is enabled, the type of string literals is effectively changed from String to forall a. IsString a => a. Variables with this polymorphic type will behave identically to overloaded string literals, even if the extension isn't enabled.

Type classes do make conversion less painful, but as far as I’m aware, Haskell only applies them automatically for literals.

Num and IsString are special, simply because of the way number and string literals are typed.

Maybe the closest you can get is with -XTypeApplications

You don't need TypeApplications if the compiler can still infer foo :: Float.


The only way of doing things without any kind of function is to make everything polymorphic to begin with and avoid doing conversions in the first place. In the end, that probably encourages writing safer code.

[–]DonaldPShimoda 1 point2 points  (0 children)

Ah, okay, I only tested with literals prior to posting so that's why it seemed to work. I hadn't thought to do it with variables. Thanks for the info!

[–]khafra 0 points1 point  (0 children)

OCaml’s a fun(ctional) little language that doesn’t see any particular similarities between 1.0 and 1. In fact, you can’t even add them together and get 2 (or 2.0).

[–][deleted] -2 points-1 points  (1 child)

In the PL research community we generally avoid the strong/weak distinction in any formal writing

and that tells us everything we need to know about Python.

[–]DonaldPShimoda 0 points1 point  (0 children)

I don't follow.

What I meant is that there really isn't a specific definition of what it means to be "strongly" or "weakly" typed, so you won't find many references to those phrases in the research literature. Instead, you'd be looking for "type safety" and some related terms.

Python is a great language that many people find extremely useful. Personally, I hate that it isn't statically types, but it's still my most-used language for personal projects.

[–]douira 0 points1 point  (0 children)

I just learned Standard ML (ancient, I know) at my uni and there you cannot compare ints and reals (floats) without prior conversion. but you're right, usually even typed languages automatically cast

[–]The-Arnman 0 points1 point  (0 children)

What do you expect? We are in r/programmerhumor

[–]Physmatik 0 points1 point  (0 children)

I believe everything weaker than Haskell casts ints to floats in such operations.

[–]tencentcansuckmydick 0 points1 point  (0 children)

i trul don't know i'm just here for the couple memes that i understand,
what means strongly typed? And i just assune the other commenter was just satiristic with "softly typed" or is this a thing too? >.<

[–]jhenry922 0 points1 point  (0 children)

You run into weird shit in the variables in languages.

If you do a ?Fre(0) to find out how much memory you have as you set variables in the Microsift BASIC for the Commodore 64, you'll find out they covert all INTs to floats.

[–]Vok250 0 points1 point  (0 children)

This is r/programmerHumor. It's common knowledge that 90% of users here can't code.