31
$\begingroup$

Note 1: My question is not about the factorial function. It's about "simple math functions" that high-school level pocket calculators can do, but most programming languages cannot do without calling a library (square roots, logarithms, trigonometric functions, factorials, etc.).

Note 2: Comments and answers have said things like "by providing a factorial out-of-the-box you'd deprive CS teachers of their favorite recursive-programming example". While I agree that this joke is clever, it's unlikely that the developers of Java intentionally didn't include a factorial function so that CS teachers could have something to teach, so this isn't the type of answer I'm seeking.


Preamble

I understand that not all programming languages can be like MATLAB, which by default allow users to write factorial(10) for 10!, sqrt(10) and for $\sqrt{10}$. Indeed, MATLAB, Mathematica, Maple, and other similar platforms, take up considerably more hard drive space than most compilers, even when combined with the space taken up by a typical heavy-weight IDE.

However, even some of the least sophisticated calculators have a button for these simple functions.

Why would so many languages like Java and C++ (see this StackOverflow thread if interested) not have a built-in way (i.e. without using a library) to do the basic math that simple calculators can do?

Related questions based on prior research:

  • Is there any particular reason to only include 3 out of the 6 trigonometry functions? (but the answers to that do not answer the question here).
  • Why programming languages does not have factorial function? (the answers say that such functions are not used much, that they would be considered "bloat", and that language designers want to avoid putting a flag in the ground that says "this is how it's done"; however my opinion is that these functions are common enough to be included in pocket calculators which have fewer KB of RAM than a computer, so they are not much "bloat", and if there's already a flag in the ground for multiplication and exponentiation I don't see why there can't be one for sin() or factorials).
$\endgroup$
2
  • 2
    $\begingroup$ Comments have been moved to chat; please do not continue the discussion here. Before posting a comment below this one, please review the purposes of comments. Comments that do not request clarification or suggest improvements usually belong as an answer, on Programming Language Design and Implementation Meta, or in Programming Language Design and Implementation Chat. Comments continuing discussion may be removed. $\endgroup$
    – Michael Homer
    Commented Nov 13, 2023 at 19:29
  • 9
    $\begingroup$ I think this question would be improved by explaining why you think programming languages should have the same features as calculators. Programming languages are not calculators: They are much more precisely specified, and are used for much more general tasks than calculators are. $\endgroup$ Commented Nov 14, 2023 at 11:10

11 Answers 11

38
$\begingroup$

The bottom line is you can't have everything.

The "elusive eight" statistical functions referenced in another answer might be absolutely essential to statisticians, but as a web application developer I've not only never needed them, I've never heard of any of them. You might say, "well, you have now, so let's add them", but it's never that simple.

Even a very simple function needs many decisions:

  • What are the types of its input and output?
  • What option flags or variations should be considered?
  • What are its failure cases, and how should those be handled?
  • Is any care needed to make it re-entrant, thread-safe, interruptible, etc?
  • Will the implementation, or even the behaviour, vary between target systems?

That means every function has a cost, and the closer you put something to the core of a language, the higher the cost.

If you make the function intrinsic to the language, all of these have to be decided as part of the language design. Once defined, they are very hard to change; and once implemented, bugs have to be fixed by changes to the core language. If you are defining a language specification, every such function puts more work on those actually implementing it.

There are two main ways around this: you can specialise, or you can modularise.

If you want to design a pocket calculator for engineers, you might spend weeks hand-crafting integrated circuits for trigonometry and logarithms; that doesn't mean those are easy functions to implement, it means they are important for the use case you're targeting. In the same way, you can write a language optimised for anything from proving mathematical theorems to calculating the probability of complex dice rolls. In each case, the things you leave out are as much a part of the definition as the things you put in.

If you don't want to specialise, you need some way of managing the vast range of different use cases someone might want to apply your language to. The principle way to do that is to make it extendable - allow the user to provide libraries of additional functions, with as little penalty as possible.

Ideally, it should be possible for those libraries to choose between optimised implementations, taking advantage of specific features of the target architecture; or portable ones, written with primitives provided by the language itself. That way, application authors can make use of either version without rewriting their code.

Finally, that brings us round to "standard libraries", which are a compromise between language intrinsics and third-party extensions: they use the mechanism of the language's extension system, but are standardised across implementations. It's still costly to add functions to these, but easier to provide things like "backports" or "polyfills" for old or incomplete implementations, or work around a bug in the CPU's implementation of a crucial instruction.

The ability to import a math or stats namespace with extra functions when you need them, is actually far more powerful than having a 1000-page language design document which tries to define every possible operation as an intrinsic keyword. Just as distributing a package containing a single symlink shows the power of a generalised package manager for an OS.

$\endgroup$
68
$\begingroup$

factorial isn't useful

Imperative programming languages don't have factorial built-in because, on its own, it isn't useful.

Considering that the factorial operator only takes integers (let's not get into the Gamma function here), factorial(13) is already beyond the range of a uint32_t, and factorial(21) is beyond a uint64_t. So there aren't many inputs that make sense: if you really needed a table of the first few factorials for something, it'd make sense to program just that: a lookup table, rather than a function.

Generally, anywhere that you'd use a factorial in mathematical theory, once you actually need to use it in practice it's generally paired with something else to cancel out most of the magnitude of it - say, dividing by a smaller factorial. If you just wrote out factorial(103) / factorial(100) in a "normal" programming language you'd get an overflow error, when what you meant was 103 * 102 * 101.

Analysis languages - things like Mathematica - will have a factorial function built in because they can intelligently work out how to simplify the expression for you without running into overflows, and because they're built for situations where you're more likely to want to use that function directly.

On the other hand, when it comes to being taught the basics of programming, factorial is a perfect implementation exercise: it's simple, it demonstrates basic principles, it demonstrates recursion, it's easy to verify that the result is correct by hand.

Most of the simple, useful, and hard-to-implement math functions are built-in. And those that aren't will generally have a third-party library available for it. If something's not implemented by default, it's generally because it's easy to implement in terms of what is available, or because you need to tailor it to your use case in all but the most trivial of situations.

$\endgroup$
1
33
$\begingroup$

Ultimately it comes down to convention and opinion: languages can't have a builtin for everything, and generally developers prefer to have smaller standard libraries with "conventional" functions for a variety of reasons (smaller footprint, less to maintain, better third-party implementations). Factorial just isn't a "conventional" function.

Anecdotally, I've written many programs in my lifetime and can't remember the last time I've ever needed factorial. Despite what some people claim, most general-purpose programming doesn't involve anything beyond basic arithmetic, even exponents are rare.

Another reason factorial isn't builtin is that the optimal implementation (assuming tail call recursion) is very trivial and only a few lines of code; it takes 10 seconds to write and the footprint is almost nothing. The opposite is why builtins like sin and cos exist even though they're rarely used.

You'll also notice other languages' standard libraries missing seemingly-common functions, like shuffle, binary_search (and binary search trees), and regular expressions. shuffle and binary_search are simple enough for developers to write themselves, and regular expressions are better suited for third-party libraries due to having many implementations with different semantics.

$\endgroup$
18
  • 11
    $\begingroup$ Most standard libraries have sqrt, log, and basic trig (sin, cos, tan, arcsin, arccos, arctan, atan2). It's also true that some "conventional" standard library functions like powers are trivial and rare; enough so that there's probably no good reason why they are included but others like factorial isn't. $\endgroup$
    – tarzh
    Commented Nov 13, 2023 at 5:36
  • 18
    $\begingroup$ @NikeDattani "basic math" is generally addition, subtraction, division, and multiplication. Anything else is much less common. Of course there are going to be cases where you use those other things all the time (eg, doing something dealing with determining trajectories or orbits), but those are more specialized areas. I'm a bit surprised at your claim of not having used a while loop in a long time. $\endgroup$
    – Herohtar
    Commented Nov 13, 2023 at 5:37
  • 3
    $\begingroup$ This section is also relevant: en.m.wikipedia.org/wiki/… $\endgroup$
    – Herohtar
    Commented Nov 13, 2023 at 5:42
  • 5
    $\begingroup$ @JackAidley factorials are used by people who have to solve combinatorics problems, which in turn may be part of computing probabilities in complex models. I can find ~11 thousand hits on GitHub code search for from math import factorial in Python projects. $\endgroup$ Commented Nov 13, 2023 at 7:28
  • 9
    $\begingroup$ "The opposite is why builtins like sin and cos exist even though they're rarely used." - Most languages do not have sin and cos as part of the language. They are usually part of the standard library, along with more esoteric functions (such as factorial). $\endgroup$
    – marcelm
    Commented Nov 13, 2023 at 12:56
25
$\begingroup$

There are three places where a language designer can choose to place some functionality:

  • language core (e.g. operator)
  • standard library
  • leave it for third-party libraries

Putting something into the language core has a huge cost (define the syntax and semantics, have a parser, have a special path for it in the code generator, have support in tools like IDEs etc.), so you do it only for features that you consider essential for a broad range of users.

Putting it into the standard library is less demanding, as from source code structure, it is "just" one more available function. But being "standard", you require each and every implementation of the language now and in the future to support it.

Leaving it to third-party is the easiest way, if the "base language" allows for a decent implementation. This is surely true for factorial, but e.g. close to impossible for things like multi-threading.

But even your factorial examples has its own twists. Already 21! is so large a number that it no longer fits into a standard long integer.

So what would you expect the "built-in" factorial to do? Produce nonsense for anything beyond 20? Return variable-length integers (e.g. BigInteger in Java)? Does your language core (not the standard library) even have such integers?

So, any "normal" (long integer) factorial function can do nothing more than return one of 21 fixed, well-known values, and can be simply replaced by a pre-filled 21-elements array, with values e.g. found in Wikipedia's Factorial entry

And by providing an factorial out-of-the-box you'd deprive CS teachers of their favorite recursive-programming example.

$\endgroup$
3
  • 3
    $\begingroup$ Also add: "Putting something into the language core also means you "lock in" the semantics, including the performance/accuracy trade-offs, and algorithms. It also means nobody can ever make a replacement with that name." "Putting it into the standard library means the semantics are slightly easier to change, and that replacements with the same name can be added later as replacements." (C++'s rand being an excellent example) "Leaving it to third-parties means the samantics are trivial to change, and offer infinite selection of performance/accuracy trade-offs" $\endgroup$ Commented Nov 13, 2023 at 22:17
  • 1
    $\begingroup$ Even with library fumctions you have a choice. You may require some use math or #include <math.h> or you can just let them available by default as built-ins. In Fortran the basic math functions, including the gamma function some Besel functions and similar, are intrinsic, you do not have to import any module (for certain more specialized things like IEEE754 stuff one does actually import a module with use ieee_arithmetic and similar). $\endgroup$ Commented Nov 14, 2023 at 18:32
  • 2
    $\begingroup$ In Swift, + - * / are not even part of the language, but part of the standard library. Same as && || ! and others. $\endgroup$
    – gnasher729
    Commented Nov 15, 2023 at 1:02
9
$\begingroup$

most programming languages cannot do without calling a library (square roots, logarithms, trigonometric functions, factorials, etc.).

The bolded part (emphasis in original) is really the key here. Sure, these functions aren't in the global namespace, and they aren't privileged by having operator syntax (e.g. 5! instead of factorial(5). But except for factorial (which as mentioned elsewhere, you rarely want to actually evaluate), all of these functions are in the standard library of any respectable general-purpose language.

So the question is really why to have these functions behind a qualified name or an import. But it costs the programmer almost no extra work to write Math.sqrt instead of sqrt, or import static Math.*; if really necessary; so there is very little practical difference, and therefore little justification is needed.

Avoiding pollution of the global namespace is a sufficient reason. Especially nowadays, IDEs offer code completion hints for names that are in scope, and the vast majority of programmers don't want to be offered the sin function every time they write s and press Tab. It would be especially annoying if every time you wanted to log a warning or an error, you were offered the math functions log, log2 and log10.

$\endgroup$
11
  • 1
    $\begingroup$ Why is it ok to have exponents (e.g. 5**3) built into Python without the need for a library, but not trig functions? I totally disagree that having to type Math.sin costs nothing, because it makes code unnecessarily long and ugly. Those import statements also add up, to the point where files often have so many to scroll through in order to just read the first line of the "actual" code. $\endgroup$ Commented Nov 13, 2023 at 18:31
  • 6
    $\begingroup$ @NikeDattani So you disagree with something I haven't said: I said it costs very little, not that it costs nothing. And personally I think Math.sin is more readable than just sin, because many people will not know that sin by itself is a math function. Your question "why is it OK?" about ** in Python makes little sense to me ─ it is OK either way, of course, the rationale for having it one way is not because the other way wouldn't be "OK". $\endgroup$
    – kaya3
    Commented Nov 13, 2023 at 18:38
  • 2
    $\begingroup$ @NikeDattani: Python's use of ** for exponentiation dates back to FORTRAN, and having code built into the compiler to recognize patterns like (EXPRESSION)**2 was simpler and more flexible than having a special syntax for things like "compute square of value". A more interesting question might be why LOG2 and EXP2 functions aren't more common, since the easiest way to perform a LOG or EXP function would be to normalize the operand into the range 1..exponent (for LOG) or 0..1 (for EXP), and normalization is easier with an exponent of 2 than with any other. $\endgroup$
    – supercat
    Commented Nov 13, 2023 at 21:02
  • 1
    $\begingroup$ @supercat Is that really relevant? Some of those functions Fortran will actually use the libmath.so of the corresponding C compiler. Who cares? The Fortran compiler will just link it. The con is that when a linking C program using the gfortran command you can actually ommit many of the libraries you have to list when linking with gcc because they are being added automatically. $\endgroup$ Commented Nov 14, 2023 at 18:40
  • 2
    $\begingroup$ @VladimirFГероямслава: FORTRAN was invented a loooong time ago, and it had to be usable on a machine which had no mass storage other than a punched card reader and a card punch. Having a little bit of bespoke logic to either load routines like SQRT from punched cards or else skip over them, based upon whether a program used them, would have been cheaper than trying to have a general-purpose linker which could operate without a tape or disk drive. The notion of a standard library makes sense on machines with disk drives, but FORTRAN predates such technologies. $\endgroup$
    – supercat
    Commented Nov 14, 2023 at 19:30
9
$\begingroup$

It's almost entirely down to history of programming languages. For example, the Elusive Eight are generally not included in languages because they were not included in C, and one can argue that this is because they were not present in BCPL, B, FORTRAN, COBOL, ALGOL, etc.

From a language-design perspective, numerical methods are usually based around IEEE 754, and this means that even basic transcendental operations are usually kicked out to a library, as they aren't defined by the standard.

Aside: on Python

Namespaces are one honking great idea – let's do more of those! ~ Tim Peters

Python has three features that aren't universal:

  • Imports may occur mid-module
  • Imported modules can access foreign or native code
  • Functions can inspect the implementation details of arguments

Benefiting from these features, Python sensibly uses math and friends to organize code into namespaces, so that the global scope is less polluted. Your design requirements around scoping and naming will constrain your day-to-day syntax, including syntax for numerical methods.

I designed the Double and Int types for Monte, a dialect of E designed to integrate various ideas from Python. We did not want to make any of those three assumptions, and we didn't want to pollute global scopes, so instead all of the necessary functionality is baked into methods on each type's classes. This forced the various mentioned functions to all be methods:

  • squareRoot
  • exponential, logarithm with optional base
  • sine, cosine, tangent, cosecant, secant, cotangent, arcSine, etc.
  • two-argument arcTangent

And also the Elusive Eight:

  • cumulativeBeta, quantileBeta
  • cumulativeGamma, quantileGamma
  • cumulativeNormal, quantileNormal
  • besselFirst

Many of those were available because Monte is built upon Python. That's the core of my first paragraph: we build upon the standard libraries of our host languages. (Source code for these methods is available here.)

Aside: on numerical methods

I also included a method euclidean in Monte. This method computes the Euclidean distance for a triangle. In Python code, a user might write:

def euclidean(x, y): return math.sqrt(x * x + y * y)

However, this is not very accurate! I used Herbie, which used to tell me to do this (ASCII art), but currently recommends using the builtin hypot, a procedure/template/macro available throughout the C/C++ world. Here is a good example of the main tradeoff to consider:

  • You implement the numerical algorithms. Your users get to benefit from direct good support for each algorithm, with high-quality results and decent performance, but they are limited to what you choose to implement. -- or --
  • Your users implement the algorithms. They have to work through arcane whitepapers and DLMF, but they get to implement whatever they want and have great performance.

You also mentioned Kahan summation. This is a good example of something I made users implement in high-level code. Why? Because I don't want to give an opinion on how a list of numbers should be summed! I want the user to choose their own summation algorithm, and then to fold generically over it. Here is more advice for language designers: It's less work for me and more control for the user.

$\endgroup$
3
  • $\begingroup$ Comments have been moved to chat; please do not continue the discussion here. Before posting a comment below this one, please review the purposes of comments. Comments that do not request clarification or suggest improvements usually belong as an answer, on Programming Language Design and Implementation Meta, or in Programming Language Design and Implementation Chat. Comments continuing discussion may be removed. $\endgroup$
    – lyxal
    Commented Nov 14, 2023 at 1:32
  • 4
    $\begingroup$ What you describe as "assumptions" of Python are IMHO better described as "features": it's not that Python assumes that some other part of the system lets a program do those things, but rather, Python itself enables programs to do those things. $\endgroup$
    – ruakh
    Commented Nov 14, 2023 at 7:23
  • 1
    $\begingroup$ »However, this is not very accurate« is somewhat misleading – it works fine most of the cases (your code uses the exact same formula then), just for extreme cases it does something else. (Also, I wonder why your MIN has a different order of magnitude than your MAX?) $\endgroup$ Commented Apr 28 at 22:47
4
$\begingroup$

Processors have several units, the most important for this discussion being the ALU, and its coprocessor, the FPU. The ALU is the part of the processor that does math. It has at most four math operations: addition, subtraction, multiplication, and division (note: subtraction, multiplication, and division are mutations of addition). As an example, RISC processors only have addition, subtraction, and multiplication, so if you want to divide, you have to use other tricks. The language needs to address those on a per-target basis.

After that, languages have to decide if they're going to support the FPU, which has functions like $cos(x)$ and $2^x-1$. The FPU only has a limited amount of space, so engineers choose the most popular functions they can optimize. There's something like 20-30 of these function in a typical FPU, plus some useful constants like $\pi$. The main functions on an FPU are centered around trigonometric functions, because those are the most useful functions outside of the four basic math operations.

In a typical language, the core syntax assumes a functional ALU, and provides syntax that will directly convert to machine code for a platform. For most functions outside of that, the programmers will often choose to write a library, mostly so they don't have to keep recompiling the compiler itself. The compiler may need to provide a $cos(x)$ function in software if there is no FPU or the FPU does not support it. Same goes for all the other trig functions. Executing functions in software is always slower than hardware, so the language should prioritize using hardware resources when available. Everything else will need to be in a library, no matter if it is built-in or not, because there is no hardware support for it.

Also, your question assumes that storage space is apparently unlimited across all devices. As a simple counterexample, consider this: you can write a program in C that will run with 32kb of ROM and 2kb of RAM (the classic NES system), but you cannot, to the best of my knowledge, do so in Java. Java is a great language, but doesn't fit all sizes. C is also a great language, but may not be the best in certain domain-specific languages. And that's the point. The design of the language is meant to fit a certain domain.

I've written code in dozens of languages over the last 35 years or so, and I can say that, with some confidence, most programs don't need trig functions. Also, I've never used factorial outside of learning how recursive functions work. The industry agrees--no processor out there that I'm aware of has native support for factorial calculation, so every language that provides it does so through an algorithm in a library.

There's also something to say for special exceptions. The more things you provide as an automatic built-in, the more exceptions you're making for the general purpose of the compiler. A more complicated compiler means better chances for bugs. By putting some of the more advanced math functions in a library, the cleaner your compiler will be.

I could make an argument for FPU-supplied functions being built-in, particularly $\sqrt{x}$ and such that are wildly useful, but again, they're kind of limited, as you don't even have $\sqrt[3]{x}$ in any FPU, so you'd still be stuck with writing custom logic to handle these kinds of operations. And, as I said previously, there's no guarantee you'll have an FPU to lean on for these operations. Aside from that, we only have so many characters on a standard keyboard, so at some point, someone has to draw a line about what built-in options we should have.

So, you could say that the reason why most languages don't include these by default is that hardware support is only "guaranteed" for about 3-4 mathematical operations, and everything else might have to be emulated in software, and that is easier done with a library framework. It is simply impractical to include more advanced maths when they're unnecessary most of the time. Most programs only need basic math (the main four), and if you need more, you're going to end up with a library of useful functions that can be better ported as a library rather than a core compiler feature.

$\endgroup$
4
  • $\begingroup$ The language does not have to decide to support an FPU; it can simply specify the operations and leave the implementation to the implementers. $\endgroup$ Commented Nov 13, 2023 at 6:16
  • $\begingroup$ I would like to upvote this, because it gets an important aspect of why the dividing line between "language" and "library" is where it is, but it contains a serious error. FPUs generally are just like ALUs, except that they implement floating-point instead of integer arithmetic. They don't provide anything besides the four basic math operations (add, subtract, multiply, divide), maybe square root, and ancillary functions like "round". Some historical designs didn't even include multiply and divide! The 80387, with its collection of logarithmic and trigonometric functions, is an outlier. $\endgroup$
    – zwol
    Commented Nov 14, 2023 at 18:22
  • $\begingroup$ In the 1990s, Dallas Semiconductor used to sell "iButton" devices that were a bit smaller than a 2032 coin cell that ran Java, using a microcontroller with IIRC 6KB of RAM. Not quite the 2KB of an NES, but still pretty tiny. $\endgroup$
    – supercat
    Commented Nov 14, 2023 at 23:14
  • $\begingroup$ Around the 68040 processor, Motorola announced that their processors were faster at calculating sin, cos etc "by hand" than using the hardware functionality in their coprocessors. @zwol Very common in modern processors is "fused multiply-add" which calculates a*b + c with a single rounding error and usually much faster than a separate multiplication and addition. $\endgroup$
    – gnasher729
    Commented Nov 15, 2023 at 1:08
4
$\begingroup$

Disclaimer: @Ralf Kleberhoff's answer provides the key distinction made here.

TL;DR: Cost and Convenience.

There are 3 possible places for functionality to be defined:

  1. Language, ie built-in.
  2. Standard library.
  3. Third-party libraries.

Should they be in the core language?

In general, the cost of maintaining any implementation in the language is high, and the convenience is low. Specifically:

  • Built-ins typically require a lot of boiler-plate: special keywords/identifiers in the syntax, special mapping from syntax to semantics, special mapping from semantics to code generation, ...
  • Furthermore, built-in are opaque. Since there is no "source" code for them, the user will not be able to step into them with their debugger, or even merely read the code to check their correctness, or get inspiration.
  • Finally, built-in are "carved in stone". Any change to their functionality may break a user1.

Hence, between being costly/inconvenient for implementers and inconvenient for users, there is a strong incentive to limit the number of built-ins in most languages.

1 Obligatory xkcd reference.

Should they be in the standard library?

Certainly, if they are worth it.

The economics are much different for a standard library -- hence why a standard library typically has many more functions than a language has built-ins:

  • The cost is not null, but way down.
  • The source code is available, both for casual inspection and debugging.
  • The functionality, though, is still "carved in stone".

For example, even though the Rust standard library is relatively lean, there's still a somewhat length list of constants defined: the cost is near nil, and constants are, well, constant over time.

Still, many standard libraries will not have factorial in mainstream languages, why?

Because even though it would probably be fairly low cost, it's also fairly useless for the target usages of these languages. This means very low user demand, and thus they are not judged to be worth the trouble.

This changes, obviously, for mathematically-focused languages.

Should they be in third-party libraries?

Yes!

None of the constraints apply to third-party libraries: they only cost to people who use them, there can be a wide variety of implementations for different trade-offs, ...

$\endgroup$
3
$\begingroup$

At least some languages were designed to be small. Pascal was meant to be taught easily; C was meant to be ported easily. While the eventual size of the specification (language + libraries) may not be that different between a large language and a small language, the implementation effort is. A library can ideally simply be recompiled for a new target. These days, I suspect that the math libs are not the most portable libraries in the world because they cater to machine specific hardware; but on some old machines (my Atari ST, for example) floating point arithmetic was entirely written in software.

$\endgroup$
2
$\begingroup$

One possible reason why C does this is because C has a concept of hosted and freestanding implementations. If an implementation is freestanding, then the standard libraries do not need to be provided. This leaves only the syntax of the language to perform tasks.

One reason for this is, while virtually any CPU is going to be able to add or subtract or multiply, not all of them, or at least, not all of the ones that C aims to support, are necessarily going to have capabilities to take the sin or cos or other complex functions of numbers. These implementations can just be freestanding and not need to include the standard libraries, and still be standards compliant.

$\endgroup$
8
  • 1
    $\begingroup$ +1. Do CPU's do powers? Also, CPU's can't do if / else statements but the C language supports them, so it doesn't seem that being able to do it natively on the CPU is a requirement for the language to have a syntax for something! $\endgroup$ Commented Nov 13, 2023 at 23:07
  • $\begingroup$ @NikeDattani A CPU could do integer powers by repeated multiplication rather easily. But I imagine fractional powers, which are required to be supported by pow(), would be much more complicated to implement. As pow(2, 0.5) would have to give an approximation of radical 2. CPUs typically implement if / else using branch/conditional jump instructions. Something like compare the condition with 0, and if it is equal, jump to the end of the if body. $\endgroup$
    – CPlus
    Commented Nov 13, 2023 at 23:10
  • $\begingroup$ @NikeDattani Things that typically cannot be implemented on the CPU are typically delegated to library functions, as is the case for copying memory blocks of arbitrary size. There is typically no instruction for that, so memcpy(). $\endgroup$
    – CPlus
    Commented Nov 13, 2023 at 23:11
  • $\begingroup$ I was thinking that if / else wasn't the best example, but isn't a lot of the C language not directly doable on a CPU? Fractional powers are possible in many languages (without calling a library), even though they can't natively be done on a CPU. Also, if integer powers are easy by repeated multiplication, then wouldn't factorial functions also be very easy? $\endgroup$ Commented Nov 13, 2023 at 23:13
  • $\begingroup$ @NikeDattani Many languages that have exponentiation with fractional powers as an operator instead of a function are high-level languages whose interpreters are implemented in C and end up calling the C pow() function behind the scenes anyway. Factorial would be easy to implement, but I guess it's just not useful enough in most programs that the language designers thought it was worthwhile to require it. $\endgroup$
    – CPlus
    Commented Nov 13, 2023 at 23:16
0
$\begingroup$

Because most languages are programmed using a keyboard, and keyboards have a limited number of keys.

With the exception of APL and its derivatives- Uiua being notable for attempting to reinvent the entire operator set- languages are designed to be represented in the 7-bit ANSI character set: I'm not necessarily saying that's good.

However neither am I saying that that limitation is bad, particularly in view of the way that APL- initially relatively accessible using composed/overstruck characters- became an impenetrable mess when implementations stopped offering that as an input method.

$\endgroup$
3
  • 5
    $\begingroup$ That's not really answering the question. Built-in way doesn't mean there has to be a symbol. Operators can be made of several characters, even letters. See sizeof in C for instance. And the question would also include build-in functions, for which you don't need to import/include a library. Python for instance has a pow() function that doesn't need to be imported. $\endgroup$
    – BlackJack
    Commented Nov 13, 2023 at 18:04
  • $\begingroup$ Even == consists of two characters and it is a very widely used operator name $\endgroup$ Commented Nov 14, 2023 at 19:00
  • $\begingroup$ Swift definitely allows non-ASCII characters in operator names. So use ≠ if you like. $\endgroup$
    – gnasher729
    Commented Dec 3, 2023 at 18:48

You must log in to answer this question.

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