11
$\begingroup$

What are the downsides to having different data types for floats and integers instead of just a number data type? I know that having different ones can improve memory usage. But are there any downsides other than some programmers using the language may not like it?

Credit to Math Cat for the original A51 question

$\endgroup$

6 Answers 6

6
$\begingroup$

It's not a common case, but integral data types (sometimes known as shorts and longs) allow for integer overflow, which may be used by clever programmers (if the programming language allows it). E.g. for an unsigned 8-bit integer type, with a range from 0 to 255, a programmer may use the fact that 255 + 1 = 0. Such use should be well-documented, and I can't remember the last time I've used it, but it's definitely possible and very difficult to support with floating point numbers.

$\endgroup$
1
  • 2
    $\begingroup$ Python circumvents this by having what are sometimes called bigints — dynamically-allocated integers with no hard limit — but that comes with its own set of drawbacks. $\endgroup$
    – Bbrk24
    May 16, 2023 at 19:48
6
$\begingroup$

The main advantage of having a number type is simplicity and ease of use. Many versions of BASIC just had floating point numbers, which reduced the size of the code (given that the first home computer, which Bill Gates wrote the BASIC for, had only 4K of memory, this was very important) and made it so that beginning programmers didn't have to learn the difference between integers and floats, everything just being numbers.

Code size is no longer an issue; few deal with systems with less than 1MB of memory. When writing a language for a beginning audience, especially children, I can still see the value in simplifying that detail.

$\endgroup$
5
$\begingroup$

The problem with having only a single number type is: which number type do you pick?

For a good example of what not to do, you have to look no further than the most widely-used (depending on definition) programming language, JavaScript. JavaScript had for a very long time only a single number type, a IEEE 754-2019 / ISO/IEC 60559:2020 binary64 floating point number.

There are two problems with this. Everybody knows about the limitations of floating point with its fixed precision and accuracy, and that's what everybody is focusing on, but that's only half the story. There is another problem here: the fact that it is a binary representation.

For the majority of real-world problems, by which I mean the kinds of problems which casual programmers and business programmers encounter, we are dealing with decimal numbers. Whether that's money, distances, speeds, weights, areas, volumes, percentages, etc. And users have an intuition about those: they know that 1/3 cannot be represented in decimal, they know that π cannot be represented in any integer base, but they are very surprised by the fact that JavaScript cannot represent 0.1.

I would argue that a programming language that does not have a good reason to do so (e.g. a language specifically for cryptography) should choose base-10 as the default number representation. I would also argue that arbitrary precision decimal rationals are a better default type than either floats or integers. I.e. make 12.34 syntactic sugar for 1234/100 and 12.34% syntactic sugar for 1234/100/100.

Arbitrary precision decimal rationals will work as long as you restrict the operations to addition, subtraction, multiplication, and division, as well as exponentiation by a natural number. For most users, those should be enough. Heck, I would bet a good sum of money that a significant portion of users don't even know that an exponent can be negative or non-integer!

It gets more interesting when you get into geometry, though. You can't avoid roots and trigonometry here, so you will quickly run into irrational and transcendental numbers. Same for finance, where you will encounter logarithms at some point.

Here, you have two choices: turn your language into a CAS (with all the complexity that carries) or punt to some sort of inexact type. In the latter case, I would still argue it should be base-10, and possibly also bigger than just 64 bit.

One idea might be to keep function applications around as un-evaluated expressions. For example, if you encounter a := sqrt(2), instead of evaluating it and having to punt to an inexact type, you keep it around as an un-evaluated expression. Then, when you later encounter a**2, you can cancel out the two operations and end up with a genuine 2 instead of something that is close to, but not equal to 2.

The problem with such representations is that they are essentially programs. With all the consequences. It doesn't take much and all of a sudden, simply checking whether two numbers are equal requires checking whether two programs compute the same function, which in the general case is equivalent to solving the Halting Problem. You end up with a number representation where your numbers are super-precise and super-accurate, but you cannot even tell whether they are equal.

There are also things like floating bar or interval arithmetic.

For an example of a language that cares a lot about accurate and precise numbers, take a look at Frink.

In conclusion, I would say there should be a default type that is useful for a lot of real-world cases. It should be decimal, have arbitrary precision, and support at least rational numbers. (Of course, the in-memory representation can be optimized as required.) For operations that are not closed under the rationals, there should be alternatives with easy conversions all around.

$\endgroup$
1
  • $\begingroup$ How should "be decimal" and "support at least rational numbers" be reconciled? You mentioned 1/3 already - is the argument for that to be out of scope, or for numbers not to be representationally decimal, just syntactically decimal? $\endgroup$
    – Michael Homer
    Jul 1, 2023 at 9:03
3
$\begingroup$

Floating point numbers can often have accuracy issues when a number is too small or too big, see relevant stackoverflow.

$\endgroup$
2
$\begingroup$

I know that having different ones can improve memory usage.

It is possible to eat your cake and have it too. APL tends to expose a single number type, but internally use the most efficient internal representation available for the actual value.

$\endgroup$
2
$\begingroup$

Conversions

Ofttimes, you have a function that takes a float, but you only have integers. Especially with languages where number conversions are explicit, this can become quite verbose. For example, Kotlin has no power operator for Ints, just for Doubles and Floats. I have written the following code at least three times by now:

val x = y.toDouble().pow(z.toDouble()).toInt()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .